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

Improving API communication using GraphQL

Improving API communication using GraphQL

15bd509601f2b058f9d6edf45302dff0?s=128

Pascal de Vink

June 16, 2017
Tweet

Transcript

  1. Improving API communication Using GraphQL

  2. Who am I to say anything about it? Currently working

    at TicketSwap Worked with SOAP, REST and GraphQL in producBon
  3. Why should we care about improving APIs? A liCle history…

  4. First, there was SOAP

  5. Remember SOAP? <?xml version="1.0"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2001/12/soap-envelope" SOAP-ENV:encodingStyle="http://www.w3.org/2001/12/soap-encoding"> <SOAP-ENV:Body xmlns:m="http://www.xyz.org/quotations"> <m:GetQuotation>

    <m:QuotationsName>Microsoft</m:QuotationsName> </m:GetQuotation> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
  6. Remember SOAP? <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://schemas.xmlsoap.org/soap/envelope/" targetNamespace="http://schemas.xmlsoap.org/soap/envelope/"> <xs:element name="Envelope" type="tns:Envelope"/> <xs:complexType

    name="Envelope"> <xs:sequence> <xs:element ref="tns:Header" minOccurs="0"/> <xs:element ref="tns:Body" minOccurs="1"/> <xs:any namespace="##other" minOccurs="0" maxOccurs="unbounded" processContents="lax"/> </xs:sequence> <xs:anyAttribute namespace="##other" processContents="lax"/> </xs:complexType> <xs:element name="Header" type="tns:Header"/> <xs:complexType name="Header"> <xs:sequence> <xs:any namespace="##other" minOccurs="0" maxOccurs="unbounded" processContents="lax"/> </xs:sequence> <xs:anyAttribute namespace="##other" processContents="lax"/> </xs:complexType> <xs:element name="Body" type="tns:Body"/> <xs:complexType name="Body"> <xs:sequence> <xs:any namespace="##any" minOccurs="0" maxOccurs="unbounded" processContents="lax"/> </xs:sequence> <xs:anyAttribute namespace="##any" processContents="lax"> <xs:annotation> <xs:documentation> Prose in the spec does not specify that attributes are allowed on the Body element </xs:documentation> </xs:annotation> </xs:anyAttribute> </xs:complexType> <xs:attribute name="mustUnderstand"> <xs:simpleType> <xs:restriction base="xs:boolean"> <xs:pattern value="0|1"/> </xs:restriction> </xs:simpleType> </xs:attribute> <xs:attribute name="actor" type="xs:anyURI"/> <xs:simpleType name="encodingStyle"> <xs:annotation> <xs:documentation>
  7. None
  8. SOAP had advantages • Language neutral: can be implemented in

    any language • Pla.orm independent: can be executed on any plaHorm • Rela5vely simple: uses XML, which is easy to read and write • Scalable: uses HTTP protocol, which allows underlying plaHorms to easily scale
  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
  10. Then came REST(fullness) Roy Fielding here

  11. REST was easier • Much simpler: by reusing HTTP verbs

    and using JSON • Easy resource discovery: every resource has a unique URL
  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
  13. To compensate, HATEOS was added

  14. HATEOS fixed things • Discoverability: by reusing HTTP verbs and

    using JSON • Availability based on state: every resource has a unique URL
  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
  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" } }
  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" } }
  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" } }
  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" } }
  20. In use by Facebook since 2012 An open standard since

    2015 hIp:/ /graphql.org
  21. Also adopted by

  22. Powerful type system • Int • Float • String •

    Boolean • ID • Custom scalar • Enum • Union • List • Non-null • Interface • (Input) Object
  23. Get what you need

  24. Get many resources

  25. Developer tools

  26. Developer tools

  27. Developer tools

  28. Development libraries • Javascript • Ruby • PHP • Python

    • Java • C/C++ • Go • Scala • .Net • SwiZ • Elixir • Haskell • Lua • Elm • Clojure • ClojureScript • OCaml • Rust • R • SQL
  29. Demo time

  30. In PHP

  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']; } ], ], ]);
  32. $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']; } ], ], ]);
  33. $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']; } ], ], ]);
  34. $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']; } ], ], ]);
  35. $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']; } ], ], ]);
  36. $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);
  37. $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);
  38. $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);
  39. $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);
  40. $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);
  41. PHP demo time

  42. Our first implementation…

  43. None
  44. None
  45. Relay

  46. • Node • Viewer Required fields

  47. $queryType = new \GraphQL\Type\Definition\ObjectType([ 'name' => 'Query', 'fields' => [

    'node' => [ 'type' => \GraphQL\Type\Definition\Type::listOf($nodeInterface), 'args' => [ 'id' => ['type' => \GraphQL\Type\Definition\Type::id()], ], 'resolve' => function ($root, $args) { // Resolve the node by ID } ], ], ]);
  48. { node(id: "Q2l0eTox") { ... on City { name }

    ... on Location { name } } }
  49. { viewer { firstname lastname recentlyVisitedEvents { name } }

    }
  50. Connections • Connec5on: relaBons between models are called connecBons •

    Node: a specific data item • Edge: the relaBonship between two nodes
  51. { viewer { firstname lastname wantedListings(first:5) { pageInfo { hasNextPage

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

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

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

    } edges { cursor node { # Node details } } } } }
  55. Complexity overload

  56. Query Complexity Analysis use GraphQL\GraphQL; /** @var \GraphQL\Validator\Rules\QueryComplexity $queryComplexity */

    $queryComplexity = DocumentValidator::getRule('QueryComplexity'); $queryComplexity->setMaxQueryComplexity($maxQueryComplexity = 110); GraphQL::execute(/*...*/);
  57. Limiting Query Depth use GraphQL\GraphQL; /** @var \GraphQL\Validator\Rules\QueryDepth $queryDepth */

    $queryDepth = DocumentValidator::getRule('QueryDepth'); $queryDepth->setMaxQueryDepth($maxQueryDepth = 10); GraphQL::execute(/*...*/);
  58. N+1 problem

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

  60. 'resolve' => function ($root, $args) { return $this->dataLoader($args[‘id']); }

  61. None
  62. public function load(array $ids) : Promise { $nodes = //

    Find all nodes by id $nodeValues = // Map them to returnable values return $this->promiseAdapter->createFulfilled($eventValues); }
  63. Look at: • hIps:/ /github.com/facebook/dataloader • hIps:/ /github.com/overblog/dataloader-php Data loader

  64. One more thing

  65. Mutations

  66. $mutationType = new \GraphQL\Type\Definition\ObjectType( [ 'name' => 'Calc', 'fields' =>

    [ 'sum' => [ 'type' => \GraphQl\Type\Definition\Type::int(), 'args' => [ 'x' => ['type' => \GraphQl\Type\Definition\Type::int()], 'y' => ['type' => \GraphQl\Type\Definition\Type::int()], ], 'resolve' => function ($root, $args) { return $args['x'] + $args['y']; }, ], ], ] );
  67. $mutationType = new \GraphQL\Type\Definition\ObjectType( [ 'name' => 'Calc', 'fields' =>

    [ 'sum' => [ 'type' => \GraphQl\Type\Definition\Type::int(), 'args' => [ 'x' => ['type' => \GraphQl\Type\Definition\Type::int()], 'y' => ['type' => \GraphQl\Type\Definition\Type::int()], ], 'resolve' => function ($root, $args) { return $args['x'] + $args['y']; }, ], ], ] );
  68. $mutationType = new \GraphQL\Type\Definition\ObjectType( [ 'name' => ‘Mutation', 'fields' =>

    [ 'sum' => [ 'type' => \GraphQl\Type\Definition\Type::int(), 'args' => [ 'x' => ['type' => \GraphQl\Type\Definition\Type::int()], 'y' => ['type' => \GraphQl\Type\Definition\Type::int()], ], 'resolve' => function ($root, $args) { return $args['x'] + $args['y']; }, ], ], ] );
  69. $mutationType = new \GraphQL\Type\Definition\ObjectType( [ 'name' => ‘Mutation', 'fields' =>

    [ 'sum' => [ 'type' => \GraphQl\Type\Definition\Type::int(), 'args' => [ 'x' => ['type' => \GraphQl\Type\Definition\Type::int()], 'y' => ['type' => \GraphQl\Type\Definition\Type::int()], ], 'resolve' => function ($root, $args) { return $args['x'] + $args['y']; }, ], ], ] );
  70. $mutationType = new \GraphQL\Type\Definition\ObjectType( [ 'name' => ‘Mutation', 'fields' =>

    [ 'sum' => [ 'type' => \GraphQl\Type\Definition\Type::int(), 'args' => [ 'x' => ['type' => \GraphQl\Type\Definition\Type::int()], 'y' => ['type' => \GraphQl\Type\Definition\Type::int()], ], 'resolve' => function ($root, $args) { return $args['x'] + $args['y']; }, ], ], ] );
  71. 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
  72. 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
  73. Resources • hCp:/ /graphql.org • hCps:/ /github.com/chentsulin/awesome-graphql • hCps:/ /www.learnrelay.org