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. Pascal de Vink Organiser at AmsterdamPHP So8ware engineer at TicketSwap

    Worked with SOAP, REST and GraphQL in producBon @pascaldevink
  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> '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 </xs:documentation> </xs:annotation> <xs:list itemType="xs:anyURI"/> </xs:simpleType> <xs:attribute name="encodingStyle" type="tns:encodingStyle"/> <xs:attributeGroup name="encodingStyle"> <xs:attribute ref="tns:encodingStyle"/> </xs:attributeGroup> <xs:element name="Fault" type="tns:Fault"/> <xs:complexType name="Fault" final="extension"> <xs:annotation> <xs:documentation>Fault reporting structure</xs:documentation> </xs:annotation> <xs:sequence> <xs:element name="faultcode" type="xs:QName"/> <xs:element name="faultstring" type="xs:string"/> <xs:element name="faultactor" type="xs:anyURI" minOccurs="0"/> <xs:element name="detail" type="tns:detail" minOccurs="0"/> </xs:sequence> </xs:complexType> <xs:complexType name="detail"> <xs:sequence> <xs:any namespace="##any" minOccurs="0" maxOccurs="unbounded" processContents="lax"/> </xs:sequence> <xs:anyAttribute namespace="##any" processContents="lax"/> </xs:complexType> </xs:schema>
  3. 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
  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: truly unique URL for every resource
  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. $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);
  17. Query: type: object config: description: A test query. fields: echo:

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

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

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

    type: String args: message: type: String resolve: "@=resolver('echo', [args])"
  21. Query: type: object config: fields: node: builder: Node builderConfig: nodeInterfaceType:

    Node idFetcher: '@=service("node_resolver").resolveByGlobalId(value, request)'
  22. Connections • Connec5on: relaBons between nodes are called connecBons •

    Edge: the relaBonship between two nodes • Node: a specific data item
  23. 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])"
  24. public function load(array $ids) : Promise { $nodes = //

    Find all nodes by id $nodeValues = // Map them to returnable values return $this->promiseAdapter->createFulfilled($eventValues); }
  25. Mutation: type: object config: fields: sum: type: Int args: x:

    Int y: Int resolve: "@=mutation('sum', [args])"
  26. originalPrice: type: 'Money!' description: 'The original price of the tickets.'

    deprecationReason: | Use the originalPrice field inside the price value instead.
  27. 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
  28. 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