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

Improving API communication using GraphQL

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest. →

Improving API communication using GraphQL

Avatar for Pascal de Vink

Pascal de Vink

June 16, 2017

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