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 Slide

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

    View Slide

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

    View Slide

  4. First, there was
    SOAP

    View 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 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 Slide

  7. View Slide

  8. 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 Slide

  9. 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 Slide

  10. Then came
    REST(fullness) Roy Fielding here

    View Slide

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

    View Slide

  12. 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 Slide

  13. To compensate,
    HATEOS was added

    View Slide

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

    View Slide

  15. 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 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 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 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 Slide

  19. 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 Slide

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

    View Slide

  21. Also adopted by

    View Slide

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

    View Slide

  23. Get what you need

    View Slide

  24. Get many resources

    View Slide

  25. Developer tools

    View Slide

  26. Developer tools

    View Slide

  27. Developer tools

    View Slide

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

    View Slide

  29. Demo time

    View Slide

  30. In PHP

    View Slide

  31. $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 Slide

  32. $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 Slide

  33. In Symfony

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  42. Symfony demo time

    View Slide

  43. Our first
    implementation…

    View Slide

  44. View Slide

  45. View Slide

  46. Relay

    View Slide

  47. • Node
    • Viewer
    Required fields

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  51. {
    viewer {
    name
    starredRepositories {
    name
    }
    }
    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  57. Complexity overload

    View Slide

  58. 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 Slide

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

    View Slide

  60. N+1 problem

    View Slide

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

    View Slide

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

    View Slide

  63. View Slide

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

    View Slide

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

    View Slide

  66. Mutations

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  70. Deprecating fields

    View Slide

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

    View Slide

  72. View Slide

  73. Monitoring

    View Slide

  74. View Slide

  75. View Slide

  76. View Slide

  77. View Slide

  78. 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 Slide

  79. 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 Slide

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

    View Slide

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

    View Slide