Introduction to GraphQL – Preston So – Origins and motivations



Introduction to GraphQL – Preston So – Origins and motivations

0 0


intro-graphql

"Introduction to GraphQL" presentation covering GraphQL origins, motivations, syntax, and schemas

On Github prestonso / intro-graphql

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

Origins and motivations GraphQL and REST GraphQL syntax Types and schemas 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"
    }
  }
}
						

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

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

Introduction to GraphQL Preston So prestonso • @prestonso • preston.so