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

Improving API Communication using GraphQL - Symfony Live 2017

Improving API Communication using GraphQL - Symfony Live 2017

Pascal de Vink

October 20, 2017
Tweet

More Decks by Pascal de Vink

Other Decks in Programming

Transcript

  1. Improving API
    communication
    Using GraphQL

    View full-size slide

  2. Pascal de Vink
    Organiser at AmsterdamPHP
    So8ware engineer at TicketSwap
    Worked with SOAP, REST and GraphQL in producBon
    @pascaldevink

    View full-size slide

  3. Why should we care
    about improving APIs?
    A liFle history…

    View full-size slide

  4. First, there was
    SOAP

    View full-size slide

  5. Remember SOAP?

    xmlns:SOAP-ENV="http://www.w3.org/2001/12/soap-envelope"
    SOAP-ENV:encodingStyle="http://www.w3.org/2001/12/soap-encoding">


    Microsoft



    View full-size slide

  6. Remember SOAP?
    targetNamespace="http://schemas.xmlsoap.org/soap/envelope/">
























    Prose in the spec does not specify that attributes are allowed on the Body element















    'encodingStyle' indicates any canonicalization conventions followed in the contents of the containing
    element. For example, the value 'http://schemas.xmlsoap.org/soap/encoding/' indicates the pattern
    described in SOAP specification











    Fault reporting structure















    View full-size slide

  7. SOAP had advantages
    • Language neutral: can be implemented
    in any language
    • Pla.orm independent: can be executed
    on any plaKorm
    • Rela5vely simple: uses XML, which is
    easy to read and write
    • Scalable: uses HTTP protocol, which
    allows underlying plaKorms to easily
    scale

    View full-size slide

  8. And some disadvantages
    • Slooooooooow: uses XML, which is was
    slow to parse, and debug
    • Depends on WSDL: needs a meta
    service to describe it’s uses, but is
    tedious to write
    • XML is hard to read: manually reading a
    soap envelop and response is not for
    the faint of hart

    View full-size slide

  9. Then came
    REST(fullness) Roy Fielding here

    View full-size slide

  10. REST was easier
    • Much simpler: by reusing HTTP verbs
    and using JSON
    • Easy resource discovery: every resource
    has a unique URL

    View full-size slide

  11. But left some parts out
    • Resource discovery was s5ll hard: a
    client had to know the url
    • Redundant and unneeded informa5on:
    a resource exposes everything it knows,
    always
    • No batching support: 1 request for each
    resource
    • Limited by HTTP verbs: not all
    mutaBons can be mapped

    View full-size slide

  12. To compensate,
    HATEOS was added

    View full-size slide

  13. HATEOS fixed things
    • Discoverability: by reusing HTTP verbs
    and using JSON
    • Availability based on state: truly unique
    URL for every resource

    View full-size slide

  14. But was hard
    • Hard to work with: it’s not easy to read
    all those _link fields
    • LiIle tooling support: discovery must be
    done manually
    • Focus on long-term design: which most
    people are not very good at
    • Lots of requests: as each link sBll
    requires a new request

    View full-size slide

  15. Enter GraphQL
    Describe
    your data
    Ask for what
    you want
    Get predictable
    results
    type Project {
    name: String
    tagline: String
    contributors: [User]
    }
    {
    project(name: "GraphQL") {
    tagline
    }
    }
    {
    "project": {
    "tagline": "A query language for APIs"
    }
    }

    View full-size slide

  16. Enter GraphQL
    Describe
    your data
    Ask for what
    you want
    Get predictable
    results
    type Project {
    name: String
    tagline: String
    contributors: [User]
    }
    {
    project(name: "GraphQL") {
    tagline
    }
    }
    {
    "project": {
    "tagline": "A query language for APIs"
    }
    }

    View full-size slide

  17. Enter GraphQL
    Describe
    your data
    Ask for what
    you want
    Get predictable
    results
    type Project {
    name: String
    tagline: String
    contributors: [User]
    }
    {
    project(name: "GraphQL") {
    tagline
    }
    }
    {
    "project": {
    "tagline": "A query language for APIs"
    }
    }

    View full-size slide

  18. Enter GraphQL
    Describe
    your data
    Ask for what
    you want
    Get predictable
    results
    type Project {
    name: String
    tagline: String
    contributors: [User]
    }
    {
    project(name: "GraphQL") {
    tagline
    }
    }
    {
    "project": {
    "tagline": "A query language for APIs"
    }
    }

    View full-size slide

  19. In use by Facebook since 2012
    An open standard since 2015
    hIp:/
    /graphql.org

    View full-size slide

  20. Also adopted by

    View full-size slide

  21. Powerful type system
    • Int
    • Float
    • String
    • Boolean
    • ID
    • Custom scalar
    • Enum
    • Union
    • List
    • Non-null
    • Interface
    • (Input) Object

    View full-size slide

  22. Get what you need

    View full-size slide

  23. Get many resources

    View full-size slide

  24. Developer tools

    View full-size slide

  25. Developer tools

    View full-size slide

  26. Developer tools

    View full-size slide

  27. Development libraries
    • Javascript
    • Ruby
    • PHP
    • Python
    • Java
    • C/C++
    • Go
    • Scala
    • .Net
    • SwiZ
    • Elixir
    • Haskell
    • Lua
    • Elm
    • Clojure
    • ClojureScript
    • OCaml
    • Rust
    • R
    • SQL

    View full-size slide

  28. $queryType = new \GraphQL\Type\Definition\ObjectType([
    'name' => 'Query',
    'fields' => [
    'echo' => [
    'type' => \GraphQL\Type\Definition\Type::string(),
    'args' => [
    'message' => ['type' => \GraphQL\Type\Definition\Type::string()],
    ],
    'resolve' => function ($root, $args) {
    return $root['prefix'].$args['message'];
    }
    ],
    ],
    ]);

    View full-size slide

  29. $schema = new Schema([
    'query' => $queryType,
    ]);
    $rawInput = file_get_contents('php://input');
    $input = json_decode($rawInput, true);
    $query = $input['query'];
    $variableValues = isset($input['variables']) ? $input['variables'] : null;
    $rootValue = ['prefix' => 'You said: '];
    $result = GraphQL::execute($schema, $query, $rootValue, null, $variableValues);
    header('Content-Type: application/json; charset=UTF-8');
    echo json_encode($result);

    View full-size slide

  30. composer config extra.symfony.allow-contrib true
    composer require overblog/graphql-bundle

    View full-size slide

  31. Query:
    type: object
    config:
    description: A test query.
    fields:
    echo:
    type: String
    args:
    message:
    type: String
    resolve: "@=resolver('echo', [args])"

    View full-size slide

  32. Query:
    type: object
    config:
    description: A test query.
    fields:
    echo:
    type: String
    args:
    message:
    type: String
    resolve: "@=resolver('echo', [args])"

    View full-size slide

  33. Query:
    type: object
    config:
    description: A test query.
    fields:
    echo:
    type: String
    args:
    message:
    type: String
    resolve: "@=resolver('echo', [args])"

    View full-size slide

  34. namespace App\Resolver;
    class EchoWell {
    public function resolve($input)
    {
    return $input['message'];
    }
    }

    View full-size slide

  35. services:
    App\Resolver\EchoWell:
    tags:
    - { name: overblog_graphql.resolver, method: resolve, alias: echo }

    View full-size slide

  36. services:
    App\Resolver\EchoWell:
    tags:
    - { name: overblog_graphql.resolver, method: resolve, alias: echo }

    View full-size slide

  37. Query:
    type: object
    config:
    description: A test query.
    fields:
    echo:
    type: String
    args:
    message:
    type: String
    resolve: "@=resolver('echo', [args])"

    View full-size slide

  38. Symfony demo time

    View full-size slide

  39. Our first
    implementation…

    View full-size slide

  40. • Node
    • Viewer
    Required fields

    View full-size slide

  41. Query:
    type: object
    config:
    fields:
    node:
    builder: Node
    builderConfig:
    nodeInterfaceType: Node
    idFetcher: '@=service("node_resolver").resolveByGlobalId(value, request)'

    View full-size slide

  42. {
    node(id: "MDEwOlJlcG9zaXRvcnk0NTQ2MjQ3Nw==") {
    ... on Organization {
    name
    }
    ... on Repository {
    name
    }
    }
    }

    View full-size slide

  43. Globally unique identifier
    City:12345
    Q2l0eToxMjM0NQ==
    Type name Identifier

    View full-size slide

  44. {
    viewer {
    name
    starredRepositories {
    name
    }
    }
    }

    View full-size slide

  45. Connections
    • Connec5on: relaBons between nodes
    are called connecBons
    • Edge: the relaBonship between two
    nodes
    • Node: a specific data item

    View full-size slide

  46. {
    viewer {
    firstname
    lastname
    starredRepositories(first:5) {
    pageInfo {
    hasNextPage
    }
    edges {
    cursor
    node {
    # Node details
    }
    }
    }
    }
    }

    View full-size slide

  47. {
    viewer {
    firstname
    lastname
    starredRepositories(first:5) {
    pageInfo {
    hasNextPage
    }
    edges {
    cursor
    node {
    # Node details
    }
    }
    }
    }
    }

    View full-size slide

  48. {
    viewer {
    firstname
    lastname
    starredRepositories(first:5) {
    pageInfo {
    hasNextPage
    }
    edges {
    cursor
    node {
    # Node details
    }
    }
    }
    }
    }

    View full-size slide

  49. {
    viewer {
    firstname
    lastname
    starredRepositories(first:5) {
    pageInfo {
    hasNextPage
    }
    edges {
    cursor
    node {
    # Node details
    }
    }
    }
    }
    }

    View full-size slide

  50. Complexity overload

    View full-size slide

  51. Query Complexity
    Analysis
    overblog_graphql:
    security:
    query_max_complexity: 110
    Query:
    type: object
    config:
    description: A test query.
    fields:
    echo:
    type: String
    complexity: '@=100 + childrenComplexity'
    args:
    message:
    type: String
    resolve: "@=resolver('echo', [args])"

    View full-size slide

  52. Limiting Query
    Depth
    overblog_graphql:
    security:
    query_max_depth: 10

    View full-size slide

  53. Uses batching and caching
    to efficiently load
    resources
    Data loader

    View full-size slide

  54. public function resolve($args)
    {
    return $this->dataLoader->load($args['id']);
    }

    View full-size slide

  55. public function load(array $ids) : Promise
    {
    $nodes = // Find all nodes by id
    $nodeValues = // Map them to returnable values
    return $this->promiseAdapter->createFulfilled($eventValues);
    }

    View full-size slide

  56. Look at:
    • hIps:/
    /github.com/facebook/dataloader
    • hIps:/
    /github.com/overblog/dataloader-php
    Data loader

    View full-size slide

  57. Mutation:
    type: object
    config:
    fields:
    sum:
    type: Int
    args:
    x: Int
    y: Int
    resolve: "@=mutation('sum', [args])"

    View full-size slide

  58. namespace App\Resolver;
    class Sum
    {
    public function mutate($args)
    {
    return $args['x'] + $args['y'];
    }
    }

    View full-size slide

  59. services:
    App\Resolver\Sum:
    tags:
    - { name: overblog_graphql.mutation, method: mutate, alias: sum }

    View full-size slide

  60. Deprecating fields

    View full-size slide

  61. originalPrice:
    type: 'Money!'
    description: 'The original price of the tickets.'
    deprecationReason: |
    Use the originalPrice field inside the price value instead.

    View full-size slide

  62. Conclusion
    • Hard to master: new developers might
    not be familiar with it
    • S5ll rough around the edges: libraries
    and frameworks are sBll at their infancy
    • Deep complexity problems: can be hard
    to find and solve

    View full-size slide

  63. Conclusion
    • Easy discovery: fun and easy to work
    with
    • Powerful tools: from developer tools to
    libraries and frameworks
    • Lightweight communica5on: using only
    what you need reduces overhead
    • Inline documenta5on: no more separate
    documentaBon to ship

    View full-size slide

  64. Resources
    • hFp:/
    /graphql.org
    • hFps:/
    /github.com/chentsulin/awesome-graphql
    • hFps:/
    /www.learnrelay.org
    • hFps:/
    /github.com/overblog/GraphQLBundle

    View full-size slide

  65. Thank you
    @pascaldevink
    hIps:/
    /joind.in/talk/41b30

    View full-size slide