Upgrade to Pro — share decks privately, control downloads, hide ads and more …

GraphQL in Python

GraphQL in Python

I love trying out new technologies to see if they're really worth the hype around them. There's certainly a lot of articles, conferences and tutorials about GraphQL, so is it really worth it? I think so! In this talk, I hope to inspire you to investigate GraphQL for your own use cases.

I'll be covering some of the basics behind GraphQL, including its pros & cons and the resolver pattern. I'll give a whistle-stop tour of a few libraries that are available, then introduce an example server I've put together for others to learn from. We should have time for a quick demo too!

Avatar for Alex Chamberlain

Alex Chamberlain

June 09, 2018

Other Decks in Programming

Transcript

  1. GRAPHQL IN PYTHON GRAPHQL IN PYTHON Alex Chamberlain, Team Leader

    @ Bloomberg LP @alexchamberlain Slides: Example Code: https://alexchamberlain.github.io/presentation-pylondinium-graphql-in-python/ https://github.com/alexchamberlain/city-api/
  2. OBJECTIVE OBJECTIVE Inspire you to investigate writing GraphQL servers in

    Python, including sign posting you towards open source libraries available.
  3. WHAT IS GRAPHQL? WHAT IS GRAPHQL? – GraphQL is a

    query language for your API, and a server-side runtime for executing queries by using a type system you define for your data. GraphQL isn’t tied to any specific database or storage engine and is instead backed by your existing code and data. https://graphql.org/learn/
  4. WHAT IS GRAPHQL? WHAT IS GRAPHQL? query country { country(iso:

    "US") { iso name cities { geonameID name } } }
  5. WHAT IS GRAPHQL? WHAT IS GRAPHQL? query city_by_id { london:

    city(geonameID: 2643743) { geonameID name latitude longitude population country { iso name } } new_york: city(geonameID: 5128581) { geonameID name latitude longitude country { iso name } } }
  6. GRAPHQL BASICS GRAPHQL BASICS The Schema Definition Language Root types:

    Query, Mutation and Subscription Resolvers implement the API: each field in a GraphQL schema is backed by a resolver.
  7. GRAPHQL RESOLVERS: DEFAULT GRAPHQL RESOLVERS: DEFAULT def default_resolve_fn(source, resolve_info, **kwargs):

    name = resolve_info.field_name property = getattr(source, name, None) if callable(property): return property() return property
  8. WHY SHOULDN'T WE USE GRAPHQL? WHY SHOULDN'T WE USE GRAPHQL?

    Largely a JS eco-system Many examples conflate the schema with business logic
  9. WHY SHOULD WE USE GRAPHQL? WHY SHOULD WE USE GRAPHQL?

    (Becoming) Industry Standard Strongly typed schema No more overfetching/underfetching Rapid product development Composable Rich eco-system
  10. PYTHON SUPPORT PYTHON SUPPORT 23 repositories on : GraphQL base

    implementation for Python , , : Integration with common frameworks : A generic utility to be used as part of your application’s data fetching layer to provide a simplified and consistent API over various remote data sources such as databases or web services via batching and caching. graphql-python organisation on GitHub graphql-core flask-graphql sanic-graphql graphene-django aiodataloader
  11. ILLUSTRATIVE EXAMPLE: THE CITY API ILLUSTRATIVE EXAMPLE: THE CITY API

    Lookup city by name, geoname ID or country. Lookup country by ISO name. https://github.com/alexchamberlain/city-api/
  12. THE SCHEMA THE SCHEMA type Country { iso: String! name:

    String! cities: [City!]! } type City { geonameID: Int! name: String! latitude: Float! longitude: Float! population: Int! country: Country! } type Query { country(iso: String): Country! city(name: String, geonameID: Int): City! } schema { query: Query }
  13. THE LOADER THE LOADER from aiodataloader import DataLoader from .models

    import Country class CountryLoader(DataLoader): def __init__(self, db): super().__init__() self.db = db async def batch_load_fn(self, isos): query = 'SELECT iso, name FROM country WHERE iso = any($1::text[])' rows = await self.db.fetch(query, isos) mapping = { row['iso']: Country( iso=row['iso'], name=row['name'] ) for row in rows } return [mapping.get(i) for i in isos] def resolver(self, context, resolve_info, *, iso): return self.load(iso) def context_resolver(self, context, resolve_info): return self.load(context.country_iso)
  14. THE SCHEMA (2) THE SCHEMA (2) def build_schema(): data =

    pkgutil.get_data('cities', 'cities.graphql').decode() schema = make_executable_schema( parse_schema(data), { 'Query': { 'country': loader_resolver('Country', CountryLoader.resolver), 'city': city_resolver }, 'Country': { 'cities': country_cities_resolver }, 'City': { 'country': loader_resolver('Country', CountryLoader.context_resolver) } } ) return schema
  15. THE SERVER THE SERVER from graphql.execution.executors.asyncio import AsyncioExecutor from sanic

    import Sanic import sanic_graphql @app.listener('before_server_start') async def init_graphql(app, loop): schema = build_schema() app.add_route( GraphQLView.as_view( schema=schema, executor=AsyncioExecutor(loop=loop), graphiql=True ), '/graphql', methods=['POST', 'OPTIONS'] ) class GraphQLView(sanic_graphql.GraphQLView): def get_context(self, request): return { 'db': request['db'], 'loaders': { 'Country': CountryLoader(request['db']), 'City': CityLoader(request['db']) } }
  16. CONCLUSION CONCLUSION GraphQL is fantastic way to define a contract

    between different parts of your system. The support in Python is awesome.
  17. REFERENCES REFERENCES GraphQL.org Blog: Top 5 reasons to use GraphQL

    Blog: GraphQL Server Basics: GraphQL Schemas, TypeDefs & Resolvers Explained