Presentation for introducing GraphQL to our devs.
- General info about REST and GraphQL
- Glimpse at implementing backend in Python
- Glimpse at frontend using Javascript/React + Apollo
Invented by Roy Fielding in 2000 • Resource identification (URI) • Self-descriptive messages • Stateless • Cacheable • Hypermedia as the engine of application state (HATEOAS) !4
E • Almost Level 2 • HTTP Verbs • PUT vs PATCH • HATEOAS, hate who? • Custom adaptions to request only needed fields or perform searches • Query parameters reduce cacheability !7
N & VA L I D AT I O N • No documentation whatsoever out-of-the-box • documentation inherently out of sync • Validation only as add-on JSON-Schema • tedious to write and maintain • multiple schemas for one resource a pain !12
P H Q L • Developed by Facebook in 2012, released in 2015 • Schema first! • Implementations, tools, frameworks for many languages • JS, Python, Swift, Java, C++, Scala, Perl, Haskell, Rust, Go, … • Go see for yourself: https://github.com/chentsulin/awesome-graphql • Server and client frameworks • Visual clients to interactively send queries (GraphiQL, Playground) !15
EAN! title: String! subtitle: String author: String author_user_id: Int created: Date! on_sale_date: Date price: Int imprint: String vlb_kat: String bisac: Bisac thema: [Thema!] genre: String tags: [String!] language: String } S C H E M A F I R S T enum PublicationType { POD PDF EPUB MOBI IBOOKS AUDIOBOOK SOFTWARE } type Bisac { name: String! code: String! } type Thema { name: String! code: String! } !20
def _is_ean(ean): regex = r'^978\d{10}$' return re.match(regex, ean) class EAN(Scalar): class Meta: description = 'EAN' @staticmethod def serialize(ean): assert _is_ean(ean), 'Invalid EAN "{}"'.format(ean) return ean @staticmethod def parse_literal(node): if isinstance(node, ast.StringValue) and _is_ean(node.value): return node.value @staticmethod def parse_value(value): return value C U S T O M T Y P E S !23
class Query(graphene.ObjectType): campaign = graphene.Field( Campaign, id=graphene.ID(required=True), description='Load a single campaign by id' ) campaigns = graphene.List( graphene.NonNull(Campaign), description='List of all campaigns' ) search_campaign = graphene.List( graphene.NonNull(Campaign), name_=graphene.String(name='name', required=True), description='Find campaign by name' ) def resolve_campaign(self, info, id): db = CampaignTable() result = db.get_campaign(id) return Campaign(**result) if result is not None else None def resolve_campaigns(self, info): db = CampaignTable() result = db.all_campaigns() return [Campaign(**entry) for entry in result] def resolve_search_campaign(self, _, name_): db = CampaignTable() result = db.find_campaign(name_) return [Campaign(**entry) for entry in result] I M P L E M E N T I N G Q U E R I E S type Query { campaign(id: ID!): Campaign campaigns: [Campaign!] searchCampaign(name: String!): [Campaign!] } from graphene.test import Client from backend.schema import schema client = Client(schema) def test_get_campaign(): result = client.execute(''' query { campaign(id:"16057911-5382-478a-a4b0-9e5082276893") { id name } } ''') assert result == { "data": { "campaign": { "id": "16057911-5382-478a-a4b0-9e5082276893", "name": "Krimi Herbst-Erscheinungen" } } }
N G T H E F R O N T E N D import ApolloClient from "apollo-boost"; import { graphqlApiUrl } from '../constants' export const client = new ApolloClient({ uri: graphqlApiUrl, }) import { gql } from 'apollo-boost' export const GET_ALL_CAMPAIGNS = gql` query Campaigns { campaigns { id realm_id name status created period { from_date to_date } } } ` export const CREATE_CAMPAIGN = gql` mutation CreateCampaign($realm_id: Int!, $name: String!) { createCampaign( realm_id: $realm_id name: $name ) { ok campaign { id realm_id name status created period { from_date to_date } } } } ` import React, { Component } from 'react' import { ApolloProvider } from 'react-apollo' import { client } from ‘./graphql/client' […] Export default class extends Component { render () { return ( <ApolloProvider client={client}> […] </ApolloProvider> ) } }