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

Improving API communication using GraphQL

Improving API communication using GraphQL

Pascal de Vink

June 16, 2017
Tweet

More Decks by Pascal de Vink

Other Decks in Programming

Transcript

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

    at TicketSwap Worked with SOAP, REST and GraphQL in producBon
  2. 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>
  3. 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
  4. 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
  5. REST was easier • Much simpler: by reusing HTTP verbs

    and using JSON • Easy resource discovery: every resource has a unique URL
  6. 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
  7. HATEOS fixed things • Discoverability: by reusing HTTP verbs and

    using JSON • Availability based on state: every resource has a unique URL
  8. 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
  9. 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" } }
  10. 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" } }
  11. 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" } }
  12. 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" } }
  13. Powerful type system • Int • Float • String •

    Boolean • ID • Custom scalar • Enum • Union • List • Non-null • Interface • (Input) Object
  14. Development libraries • Javascript • Ruby • PHP • Python

    • Java • C/C++ • Go • Scala • .Net • SwiZ • Elixir • Haskell • Lua • Elm • Clojure • ClojureScript • OCaml • Rust • R • SQL
  15. $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']; } ], ], ]);
  16. $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']; } ], ], ]);
  17. $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']; } ], ], ]);
  18. $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']; } ], ], ]);
  19. $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']; } ], ], ]);
  20. $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);
  21. $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);
  22. $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);
  23. $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);
  24. $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);
  25. $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 } ], ], ]);
  26. { node(id: "Q2l0eTox") { ... on City { name }

    ... on Location { name } } }
  27. Connections • Connec5on: relaBons between models are called connecBons •

    Node: a specific data item • Edge: the relaBonship between two nodes
  28. Query Complexity Analysis use GraphQL\GraphQL; /** @var \GraphQL\Validator\Rules\QueryComplexity $queryComplexity */

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

    $queryDepth = DocumentValidator::getRule('QueryDepth'); $queryDepth->setMaxQueryDepth($maxQueryDepth = 10); GraphQL::execute(/*...*/);
  30. public function load(array $ids) : Promise { $nodes = //

    Find all nodes by id $nodeValues = // Map them to returnable values return $this->promiseAdapter->createFulfilled($eventValues); }
  31. $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']; }, ], ], ] );
  32. $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']; }, ], ], ] );
  33. $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']; }, ], ], ] );
  34. $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']; }, ], ], ] );
  35. $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']; }, ], ], ] );
  36. 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
  37. 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