Introduction to GraphQL

Preston So

prestonso@prestonsopreston.so

Presented at

  • BADCamp 2015 (October 25, 2015)
  • NYC Drupal Meetup (February 3, 2016)
  • DrupalCon Mumbai Acquia mini-session (February 19, 2016)
  • Acquia webinar (March 10, 2016)
  • Drupaldelphia 2016 (April 8, 2016)

Special thanks to Sebastian Siemssen (fubhy) for collaboration on the material presented for the Acquia webinar.

Welcome!

Preston So has designed and developed websites since 2001 and built them in Drupal since 2007. He is Development Manager of Acquia Labs at Acquia and co-founder of the Southern Colorado Drupal User Group (est. 2008). Previously, Preston was Technical Lead of Entertainment Weekly at Time Inc.

What we'll cover

  1. Origins and motivations
  2. GraphQL and REST
  3. GraphQL syntax
  4. Types and schemas
  5. GraphQL and Drupal

Origins and motivations

  • What is GraphQL?
  • The Facebook open-source ecosystem
  • History of GraphQL
  • A quick introduction to React
  • React, Relay, and GraphQL

What is GraphQL?

GraphQL is a declarative query language and an application-level protocol.

What is GraphQL?

  • Like SPARQL, GraphQL describes function calls rather than actual queries to a database.
  • A GraphQL server interprets these calls and then queries the database.
  • GraphQL is entirely agnostic to the storage layer and can be used as a proxy or relay system to forward API calls.

The Facebook open-source ecosystem

React is a view-based JavaScript framework that uses a Virtual DOM to save application state and rerenders components of the view based on diffed states.

The Facebook open-source ecosystem

Relay is a framework that juxtaposes data fetching needs and React components in the same place; in other words, Relay connects view logic to queries in the back end.

The Facebook open-source ecosystem

GraphQL is a query language that enables transport- and storage-agnostic communication and returns data according to the same structure as the request query.

History of GraphQL

  • Invented during the conversion of HTML5-based mobile apps into native apps
  • Facilitates most interactions in the iOS and Android Facebook applications
  • Predates Relay by three years

History of GraphQL

Instead of placing data fetching logic in some other part of the client application – or embedding this logic in a custom endpoint on the server – we instead co-locate a declarative data-fetching specification alongside the React component.

—Nick Schrock, Facebook

History of GraphQL

  • Client requests and server payloads adhere to a shared shape.
  • The server houses the schema.
  • The client dictates what it wants the server to provide.

A quick introduction to React

Let’s take a React application that needs to fetch some fields adhering to a certain structure, such as a list of articles, each alongside the following information.

  • Article
    • Title
    • Path
    • Author
      • Name
      • Location

A quick introduction to React

React consists of components, which are rendered through JSX templates and comprise view hierarchies.

							var articleListItem = React.createClass({
  render: function () {
    return (
      
  • {this.props.article.title} {this.props.article.author.name} from {this.props.article.author.location}
  • ); } });

    React, Relay, and GraphQL

    With GraphQL, this structure can be easily mirrored on the server in both query and response.

    							var articleListItem = React.createClass({
      statics: {
        queries: {
          article: function () {
            return graphql`
              {
                article {
                  title
                  path
                  author {
                    name
                    location
                  }
                }
              }
            `;
          }
        }
      }
      // render: function () { ...
    });
    						

    React, Relay, and GraphQL

    There's a lot of power in this approach ...

    							{
      article {
        title
        path
        author {
          name
          location
        }
      }
    }
    						

    React, Relay, and GraphQL

    ... because the server’s response really does mirror the client’s desired query.

    							{
      "article": {
        "title": "Gang Jailed: Hoverboard Rampage Destroys Courthouse",
        "path": "http://usatoday.com/article/gang-jailed-hoverboard-rampage",
        "author": {
          "name": "Compu-Fax",
          "location": "Hill Valley, CA"
        }
      }
    }
    						
    Back to the Future?

    GraphQL and REST

    • Limitations of REST
    • How GraphQL resolves REST limitations
    • Why GraphQL?
    • Criticisms of GraphQL
    • Get going now

    Limitations of REST

    • Many endpoints
    • Bloated responses
    • Many round trips
    • No backwards compatibility
    • Not introspective

    Many endpoints

    Endpoints are specific to individual views or ill-suited for trees of related resources, leading to bespoke or ad-hoc endpoints.

    Bloated responses

    Due to a REST API’s changing requirements, clients that only need limited data may have to deal with increasingly large payloads destined for other clients.

    • GET /users/:id
    • GET /groups/:id
    • GET /groups/:id/users/:id

    Many round trips

    If what is fetched for your view is complex and relational, multiple client-server round trips and corresponding bootstraps will be required, delaying render.

    No backwards compatibility

    REST APIs are versioned and by nature obsolescent as client needs evolve. Queries must be rewritten against the new API.

    No introspection

    REST APIs with custom endpoints usually lack a native schema or formalized type system, making client-side tooling and validation difficult.

    How GraphQL resolves REST limitations

    • Single endpoint
    • Tailored responses
    • Fewer round trips
    • Backwards compatibility
    • Introspective

    Single endpoint

    A shared endpoint can resolve GraphQL queries into root calls and send back a single, unified response.

    Tailored responses

    Client-driven queries mean that the response is catered to the demands of the client rather than the limitations of the API.

    Fewer round trips

    A GraphQL server returns a single response to match the request structure, which is flexible enough to accommodate many possible relationships.

    Backwards compatibility

    No matter what version your most current API, your client can provide the same query to multiple versions and expect a common response.

    Introspection

    GraphQL has a native and highly extensible schema and type system.

    Why GraphQL?

    GraphQL is a common vernacular between server and client, similar enough to JSON to be easy for onboarding.

    Why GraphQL?

    GraphQL accelerates the arrival of your query’s response by preventing unneeded requests or bootstraps of the back end.

    Why GraphQL?

    GraphQL is an application-layer protocol agnostic to both its transport (the HTTP protocol is only one of many ways queries can be transmitted) and the database queried (schemas need not match).

    GraphQL is not REST ... but it can be

    • Your API is usually composed of a spectrum of different interpretations of REST.
    • GraphQL is not strictly RESTful, but you can use it with a RESt layer if you want to. However, this is not a requirement.

    GraphQL is not REST ... but it can be

    Where's the guy who's using REST correctly?

    Source: GraphQL with Nick Schrock, React London meetup (Oct. 2015)

    The changing server–client relationship

    • GraphQL evolves the server–client relationship.
    • The server publishes its possibilities; the client specifies its requirements.

    Criticisms of GraphQL

    Different requirements for views can be satisfied by provisioning additional REST endpoints for small subsets of data. But this arbitrarily adds more endpoints.

    Criticisms of GraphQL

    HTTP can handle multiple parallel network requests. But HTTP/1.1 recommends only two, while Chrome accepts six. HTTP/2 may mitigate this.

    Criticisms of GraphQL

    Content negotiation in HTTP allows clients to request multiple versions of a document at a single path. This resolves the issue of requests against API versions at differing paths.

    Criticisms of GraphQL

    HTTP has a built-in content type system that is analogous to GraphQL’s native type system and can accompany standards such as JSON Schema.

    Get going now

    Rising Stack’s GraphQL server is implemented in Node.js and has MongoDB integration: github.com/RisingStack/graphql-server

    GraphQL syntax

    • Operations and selection sets
    • Fields and aliases
    • Fragments
    • Variables
    • Directives
    • Mutations

    Operations

    GraphQL models two types of operations.

    							query {
      # Read-only fetch
    }
    mutation {
      # Write then fetch
    }
    						

    Operations

    These operations can have names, which are case-sensitive.

    							query getUser {
      # Read-only fetch
    }
    mutation changeUser {
      # Write then fetch
    }
    						

    Operations

    Anonymous queries have a shorthand.

    							{
      # Read query
    }
    						

    Selection sets

    Using Drupal's REST, you would issue a GET request with a body as follows.

    							GET /node/1?_format=json HTTP/1.1
    Host: drupaldevsite.dd:8083
    Accept: application/json
    Authorization: Basic YWRtaW46YWRtaWr=
    Cache-Control: no-cache
    						

    Selection sets

    And you would receive this response in JSON. But what if you only want the title?

    							{
      "nid": [ { "value": "1" } ],
      "uuid": [ { "value": "a8b233e0-ffff-434f-be00-ce1ac4599499" } ],
      "vid": [ { "value": "1" } ],
      "type": [ { "target_id": "page" } ],
      "langcode": [ { "value": "en" } ],
      "title": [ { "value": "Gang Jailed: Hoverboard Rampage Destroys Courthouse" } ],
      // ...
    }
    						

    Selection sets

    Selection sets are subsets of fields on an object. They are made up of \n-separated fields, which are discrete pieces of information.

    							{
      entity {
        node(id: 1) {
          title
        }
      }
    }
    						

    Selection sets

    Selection sets are subsets of fields on an object. They are made up of \n-separated fields, which are discrete pieces of information.

    							{
      "entity": {
        "node": {
          "title": "Gang Jailed: Hoverboard Rampage Destroys Courthouse"
        }
      }
    }
    						

    Fields

    Fields can describe relationships with other data. Top-level fields are typically globally accessible.

    							{
      entity {
        node(id: 1) {
          title
          created
        }
      }
    }
    						

    Fields

    Fields can describe relationships with other data.

    							{
      "entity": {
        "node": {
          "title": "Gang Jailed: Hoverboard Rampage Destroys Courthouse",
          "created": "1460089883"
        }
      }
    }
    						

    Fields

    Fields return values and can carry arguments.

    							{
      entity {
        node(id: 1) {
          title
          created
        }
      }
    }
    						

    Fields

    Fields return values and can carry arguments.

    							{
      "entity": {
        "node": {
          "title": "Gang Jailed: Hoverboard Rampage Destroys Courthouse",
          "created": "1460089883"
        }
      }
    }
    						

    Fields

    More than one argument is possible in an arbitrary order.

    							{
      entity {
        node(id: 1) {
          title
          created
          image(height: 72, width: 72)
        }
      }
    }
    						

    Aliases

    Fields can be aliased.

    							{
      content:entity {
        node(id: 1) {
          title
          created
        }
      }
    }
    						

    Aliases

    Fields can be aliased.

    							{
      "content": {
        "node": {
          "title": "Gang Jailed: Hoverboard Rampage Destroys Courthouse",
          "created": "1460089883"
        }
      }
    }
    						

    Aliases

    Aliases are also useful for disambiguation.

    							{
      content:entity {
        node(id: 1) {
          title
          created
          thumbnail:image(height: 72, width: 72)
          picture:image(height: 250, width: 250)
        }
      }
    }
    						

    Aliases

    Aliases are also useful for disambiguating identically named fields.

    							{
      "content": {
        "node": {
          "title": "Gang Jailed: Hoverboard Rampage Destroys Courthouse",
          "created": "1460089883",
          "thumbnail": "http://i.usatoday.com/img/gang-jailed-hoverboard-rampage-72x72.jpg",
          "picture": "http://i.usatoday.com/img/gang-jailed-hoverboard-rampage-250x250.jpg"
        }
      }
    }
    						

    Fragments

    Fragments allow for reusable fields. Fields in fragments are added at the same level of invocation as adjacent fields.

    							{
      entity {
        node(id: 1) {
          title
          ...customFields
        }
      }
    }
    fragment customFields on EntityNodeArticle {
      fieldAuthor
      fieldTags {
        name
      }
    }
    						

    Fragments

    Fragments allow for reusable fields. Fields in fragments are added at the same level of invocation as adjacent fields.

    							{
      "entity": {
        "node": {
          "title": "Gang Jailed: Hoverboard Rampage Desetroys Courthouse",
          "fieldAuthor": "Compu-Fax",
          "fieldTags": {
            "name": "Hill Valley"
          }
        }
      }
    }
    						

    Fragments

    Fragments declare types, which facilitate conditionally applied fields. Fragments can also be arbitrarily nested.

    							{
      entity {
        node(id: 1) {
          title
          ...customFields
        }
      }
    }
    fragment customFields on EntityNodeArticle {
      author:fieldAuthor {
        entity {
          references {
            node {
              title
              ...customAuthorFields
            }
          }
        }
      }
    }
    fragment customAuthorFields on EntityNodeAuthor {
      location:fieldLocation
      organization:fieldOrganization
    }
    						

    Fragments

    Fragments declare types, which facilitate conditionally applied fields. Fragments can also be arbitrarily nested.

    							{
      "entity": {
        "node": {
          "title": "Gang Jailed: Hoverboard Rampage Desetroys Courthouse",
          "author": {
            "entity": {
              "references": {
                "node": {
                  "title": "Compu-Fax",
                  "location": "Hill Valley, CA",
                  "organization": "USA Today"
                }
              }
            }
          }
        }
      }
    }
    						

    Inline fragments

    Inline fragments can improve code legibility and be anonymous.

    							{
      entity {
        node(id: 1) {
          title
          ... on EntityNodeArticle {
            author:fieldAuthor {
              entity {
                references {
                  node {
                    title
                    ... on EntityNodeAuthor {
                      location:fieldLocation
                      organization:fieldOrganization
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    						

    Variables and directives

    • Variables and directives are particularly important for caching and obfuscating the actual query.
    • A client-side application can use a build task to extract GraphQL queries containing variable placeholders from a configuration file tied to server-generated endpoints.
    • If you send only variables over the wire to the server rather than the entire query, only one GET is required.

    Variables

    Variables are defined at the top of and scoped to operations.

    							query getArticle($nid: Int) {
      node(id: $nid) {
        title
        ... on EntityNodeArticle {
          body
        }
      }
    }
    						

    Directives

    Directives alter execution behavior and can be used to conditionally include (@include) or exclude (@skip) fields.

    							query getArticle($published: Boolean, $nid: Int) {
      node(id: $nid) {
        title
        ... @include(if: $published) {
          body
        }
      }
    }
    						

    Directives

    Directives alter execution behavior and can be used to conditionally include or exclude fields.

    							// $published resolves to false
    {
      "article": {
        "title": "Gang Jailed: Hoverboard Rampage Destroys Courthouse"
      }
    }
    // $published resolves to true
    {
      "article": {
        "title": "Gang Jailed: Hoverboard Rampage Destroys Courthouse",
        "body": "Reckless hoverboarders careened into Hill Valley's City Courthouse late yesterday afternoon, causing serious damage to the structure.",
      }
    }
    						

    Directives

    The GraphQL spec recommends supporting @skip and @include.

    							query getArticle($published: Boolean, $authorIsAnonymous: Boolean) {
      article:node(id: 992) {
        title
        author @skip(if: $authorIsAnonymous)
        ... @include(if: $published) {
          body
          image(width: 960)
        }
      }
    }
    						

    Mutations

    In both queries and mutations, all fields apart from top-level mutation fields resolve idempotently.

    							# Pseudocode.
    mutation favoriteArticle {
      favoriteArticle(id: 992) {
        node {
          favoriteCount
        }
      }
    }
    						

    Mutations

    In both queries and mutations, all fields apart from top-level mutation fields resolve idempotently.

    							// Server performs favoriteArticle write.
    {
      "node": {
        "favoriteCount": 1022
      }
    }
    						

    Mutations

    Mutations will not be available in the initial release of the GraphQL module. This example mutation supplies a new title to a particular node before fetching the new title.

    							# Pseudocode
    mutation updateTitle {
      setTitle(title: "Gang Jailed: Hoverboard Rampage Destroys Courthouse") {
        node(id: 992) {
          title
        }
      }
    }
    						

    Mutations

    Mutations will not be available in the initial release of the GraphQL module. This example mutation supplies a new title to a particular node before fetching the new title.

    							// Before setTitle mutation
    {
      "node": {
        "title": "Youth Jailed: Marty McFly Junior Arrested for Theft"
      }
    }
    // After setTitle mutation
    {
      "node": {
        "title": "Gang Jailed: Hoverboard Rampage Destroys Courthouse"
      }
    }
    						

    Mutations

    Here is an example of what an update of a node might look like in lieu of issuing a POST request.

    							# Pseudocode
    mutation {
      updateNode(id: 123, values: {
        title: "Gang Jailed: Hoverboard Rampage Destroys More than Just Courthouse",
        body: "Breaking: More portions of downtown Hill Valley were damaged today in the recent rampage.",
        fieldTags: "Breaking news"
      }) {
        title
      }
    }
    						

    Types and schemas

    • Scalars
    • Objects
    • Object field arguments
    • Interfaces
    • Unions
    • Introspection

    Types and schemas

    In GraphQL, schemas define the capabilities of your GraphQL server — which types and directives it supports — and validate your queries.

    Scalars

    A GraphQL server should generally support these scalar types at minimum.

    • Int
    • Float
    • String
    • Boolean
    • ID (serialized as String)

    Objects

    Types are objects that are validated against and represent the full breadth of selectable fields.

    							type EntityNodeArticle {
      title: String
      body: String
    }
    						

    Objects

    Types can refer to other types you have created or recursively to itself.

    							type EntityNodeArticle {
      title: String
      body: String
      related: EntityNodeArticle
    }
    						

    Objects

    If a type object is nested within another, some field within the nested object must be selected. Therefore this query is valid.

    							{
      title
      related {
        title
      }
    }
    						

    Objects

    But this one is invalid.

    							{
      title
      related
    }
    						

    Object field arguments

    Field arguments are defined by expressing all possible arguments and expected input types.

    							type EntityNodeArticle {
      title: String
      body: String
      image(height: Int, width: Int): String
    }
    						

    Object field arguments

    Once this type object is defined, these queries are valid.

    							{
      entity {
        node {
          image(height: 100)
        }
      }
    }
    # New query
    {
      entity {
        node {
          image(height: 100, width: 100)
        }
      }
    }
    						

    Interfaces

    • Interfaces are implemented by GraphQL objects in case a field is present on sibling types.
    • EntityNode is an interface of the underlying Node object: If you have only one bundle, then the interface name becomes EntityNodeNode.

    Interfaces

    Interfaces are implemented by GraphQL objects in case a field is present on sibling types.

    							interface EntityNode {
      title: String
    }
    type EntityNodeArticle implements EntityNode {
      body: String
    }
    						

    Interfaces

    Types invoked within type objects can refer to interfaces.

    							interface EntityNode {
      title: String
    }
    type EntityNodeArticle implements EntityNode {
      body: String
      fieldAuthor: EntityNodeAuthor
    }
    type EntityNodeAuthor implements EntityNode {
      fieldLocation: String
      fieldOrganization: String
    }
    						

    Interfaces

    This allows for the fragment examples we saw earlier.

    							{
      entity {
        node(id: 1) {
          title
          ... on EntityNodeArticle {
            body
          }
        }
      }
    }
    						

    Unions

    Unions in GraphQL are objects that could be one of multiple types, such as a search result.

    							# Pseudocode
    Union Result = Node | TaxonomyTerm
    
    type Node {
      title: String
      body: String
    }
    type TaxonomyTerm {
      title: String
      vocabulary: Vocabulary
    }
    type Search {
      response: Result
    }
    						

    Unions

    This allows for us to set type-based conditions in our queries, since Result can be either type.

    							# Pseudocode
    query searchQuery {
      response {
        # The following field is only valid if all Result types have title.
        title
        ... on Node {
          body
        }
        ... on TaxonomyTerm {
          vocabulary
        }
      }
    }
    						

    Introspection

    GraphQL allows for exhaustive introspection of the schema. For example, take EntityNodeArticle.

    							interface EntityNode {
      title: String
    }
    type EntityNodeArticle implements EntityNode {
      body: String
    }
    						

    Introspection

    We can build a query selecting the built-in field __type.

    							{
      __type(name: "EntityNodeArticle") {
        name
        fields {
          name
          type {
            name
          }
        }
      }
    }
    						

    Introspection

    This returns a hierarchy of the type object.

    							{
      "__type": {
        "name": "EntityNodeArticle",
        "fields": [
          {
            "name": "title",
            "type": { "name": "String" }
          },
          {
            "name": "body",
            "type": { "name": "String" }
          },
        ]
      }
    }
    						

    Introspection

    You can also use the __schema field to access default types.

    							{
      __schema {
        types {
          name
        }
      }
    }
    						

    GraphQL and Drupal

    • GraphQL and Drupal 8
    • Next steps for GraphQL module
    • The future of GraphQL
    • Decoupled Drupal and GraphQL
    • Up for discussion

    GraphQL and Drupal 8

    Sebastian Siemssen (fubhy) was featured in the Barcelona Driesnote and is completing a GraphQL implementation on Drupal using the TypedData API.

    d.o/project/graphql

    GraphQL and Drupal 8

    GraphiQL is a GUI for testing and debugging queries.

    Video demo

    Next steps for module

    • Access control
    • Mutation support
    • Schema and type configurability

    The future of GraphQL

    • The spec is in heavy flux (no pun intended). Features are still being added.
    • Facebook plans to build more tools that natively support GraphQL.
    • WebSocket support was announced recently, and details will soon be added to the spec and to the JavaScript reference implementation on GitHub.

    Decoupled Drupal and GraphQL

    • Other module integrations: Rules
    • Client-side applications (React/Redux, etc.)
    • Request caching

    Up for discussion

    • What is the future of Web Services in Drupal in light of GraphQL?
    • Should we prioritize implementation of a GraphQL server in core?
    • What is the future of decoupled architectures in the front end with GraphQL?
    • What is the future of RESTful architectures and the server-client relationship in light of GraphQL?

    Thank you!

    prestonso@prestonsopreston.so