GraphQL Meets Drupal
Sebastian Siemssen
@thefubhy
What's inside?!
Motivation
The Limitations of REST
Overfetching
Underfetching
Multiple round trips
Versioning
...
Desired information
Full name of a person
Movie appearances (name of movie)
Home planet (name of planet)
https://swapi.co/api/people/1
Overfetching
Unless specifically designed for a given purpose, you often have to deal with needlessly large and bloated responses.
{
"name": "Luke Skywalker",
"height": "172",
"mass": "77",
"hair_color": "blond",
"skin_color": "fair",
"eye_color": "blue",
"birth_year": "19BBY",
"gender": "male",
"homeworld": "http://swapi.co/api/planets/1/",
"films": [
"http://swapi.co/api/films/6/",
"http://swapi.co/api/films/3/",
"http://swapi.co/api/films/2/",
"http://swapi.co/api/films/1/",
"http://swapi.co/api/films/7/"
],
"species": [
"http://swapi.co/api/species/1/"
],
"vehicles": [
"http://swapi.co/api/vehicles/14/",
"http://swapi.co/api/vehicles/30/"
],
"starships": [
"http://swapi.co/api/starships/12/",
"http://swapi.co/api/starships/22/"
],
"created": "2014-12-09T13:50:51.644000Z",
"edited": "2014-12-20T21:17:56.891000Z",
"url": "http://swapi.co/api/people/1/"
}
https://swapi.co/api/people/1
Additional
round trips
When fetching complex, relational data structures: In order to descent further into the object graph, multiple round trips to the server are required.
{
"name": "Tatooine",
"rotation_period": "23",
"orbital_period": "304",
"diameter": "10465",
"climate": "arid",
"gravity": "1 standard",
"terrain": "desert",
"surface_water": "1",
"population": "200000",
"residents": [
"http://swapi.co/api/people/1/",
"http://swapi.co/api/people/2/",
"http://swapi.co/api/people/4/",
"http://swapi.co/api/people/6/",
"http://swapi.co/api/people/7/",
"http://swapi.co/api/people/8/",
"http://swapi.co/api/people/9/",
"http://swapi.co/api/people/11/",
"http://swapi.co/api/people/43/",
"http://swapi.co/api/people/62/"
],
"films": [
"http://swapi.co/api/films/5/",
"http://swapi.co/api/films/4/",
"http://swapi.co/api/films/6/",
"http://swapi.co/api/films/3/",
"http://swapi.co/api/films/1/"
],
"created": "2014-12-09T13:50:49.641000Z",
"edited": "2014-12-21T20:48:04.175778Z",
"url": "http://swapi.co/api/planets/1/"
}
https://swapi.co/api/planets/1
{
"title": "A New Hope",
"episode_id": 4,
"opening_crawl": "It is a period of civil war.\r\nRebel spaceships, striking\r\nfrom a hidden base, have won\r\ntheir first victory against\r\nthe evil Galactic Empire.\r\n\r\nDuring the battle, Rebel\r\nspies managed to steal secret\r\nplans to the Empire's\r\nultimate weapon, the DEATH\r\nSTAR, an armored space\r\nstation with enough power\r\nto destroy an entire planet.\r\n\r\nPursued by the Empire's\r\nsinister agents, Princess\r\nLeia races home aboard her\r\nstarship, custodian of the\r\nstolen plans that can save her\r\npeople and restore\r\nfreedom to the galaxy....",
"director": "George Lucas",
"producer": "Gary Kurtz, Rick McCallum",
"release_date": "1977-05-25",
"characters": [
"http://swapi.co/api/people/1/",
"http://swapi.co/api/people/2/",
"http://swapi.co/api/people/3/",
"http://swapi.co/api/people/4/",
"http://swapi.co/api/people/5/",
"http://swapi.co/api/people/6/",
"http://swapi.co/api/people/7/",
"http://swapi.co/api/people/8/",
"http://swapi.co/api/people/9/",
"http://swapi.co/api/people/10/",
"http://swapi.co/api/people/12/",
"http://swapi.co/api/people/13/",
"http://swapi.co/api/people/14/",
"http://swapi.co/api/people/15/",
"http://swapi.co/api/people/16/",
"http://swapi.co/api/people/18/",
"http://swapi.co/api/people/19/",
"http://swapi.co/api/people/81/"
],
"planets": [
"http://swapi.co/api/planets/2/",
"http://swapi.co/api/planets/3/",
"http://swapi.co/api/planets/1/"
],
"starships": [
"http://swapi.co/api/starships/2/",
"http://swapi.co/api/starships/3/",
"http://swapi.co/api/starships/5/",
"http://swapi.co/api/starships/9/",
"http://swapi.co/api/starships/10/",
"http://swapi.co/api/starships/11/",
"http://swapi.co/api/starships/12/",
"http://swapi.co/api/starships/13/"
],
"vehicles": [
"http://swapi.co/api/vehicles/4/",
"http://swapi.co/api/vehicles/6/",
"http://swapi.co/api/vehicles/7/",
"http://swapi.co/api/vehicles/8/"
],
"species": [
"http://swapi.co/api/species/4/",
"http://swapi.co/api/species/5/",
"http://swapi.co/api/species/3/",
"http://swapi.co/api/species/2/",
"http://swapi.co/api/species/1/"
],
"created": "2014-12-10T14:23:31.880000Z",
"edited": "2015-04-11T09:46:52.774897Z",
"url": "http://swapi.co/api/films/1/"
}
https://swapi.co/api/films/1
https://swapi.co/api/films/2
https://swapi.co/api/films/3
https://swapi.co/api/films/6
https://swapi.co/api/films/7
{
"title": "The Empire Strikes Back",
"episode_id": 5,
"opening_crawl": "It is a dark time for the\r\nRebellion. Although the Death\r\nStar has been destroyed,\r\nImperial troops have driven the\r\nRebel forces from their hidden\r\nbase and pursued them across\r\nthe galaxy.\r\n\r\nEvading the dreaded Imperial\r\nStarfleet, a group of freedom\r\nfighters led by Luke Skywalker\r\nhas established a new secret\r\nbase on the remote ice world\r\nof Hoth.\r\n\r\nThe evil lord Darth Vader,\r\nobsessed with finding young\r\nSkywalker, has dispatched\r\nthousands of remote probes into\r\nthe far reaches of space....",
"director": "Irvin Kershner",
"producer": "Gary Kutz, Rick McCallum",
"release_date": "1980-05-17",
"characters": [
"http://swapi.co/api/people/1/",
"http://swapi.co/api/people/2/",
"http://swapi.co/api/people/3/",
"http://swapi.co/api/people/4/",
"http://swapi.co/api/people/5/",
"http://swapi.co/api/people/10/",
"http://swapi.co/api/people/13/",
"http://swapi.co/api/people/14/",
"http://swapi.co/api/people/18/",
"http://swapi.co/api/people/20/",
"http://swapi.co/api/people/21/",
"http://swapi.co/api/people/22/",
"http://swapi.co/api/people/23/",
"http://swapi.co/api/people/24/",
"http://swapi.co/api/people/25/",
"http://swapi.co/api/people/26/"
],
"planets": [
"http://swapi.co/api/planets/4/",
"http://swapi.co/api/planets/5/",
"http://swapi.co/api/planets/6/",
"http://swapi.co/api/planets/27/"
],
"starships": [
"http://swapi.co/api/starships/10/",
"http://swapi.co/api/starships/11/",
"http://swapi.co/api/starships/12/",
"http://swapi.co/api/starships/15/",
"http://swapi.co/api/starships/21/",
"http://swapi.co/api/starships/22/",
"http://swapi.co/api/starships/23/",
"http://swapi.co/api/starships/3/",
"http://swapi.co/api/starships/17/"
],
"vehicles": [
"http://swapi.co/api/vehicles/8/",
"http://swapi.co/api/vehicles/14/",
"http://swapi.co/api/vehicles/16/",
"http://swapi.co/api/vehicles/18/",
"http://swapi.co/api/vehicles/19/",
"http://swapi.co/api/vehicles/20/"
],
"species": [
"http://swapi.co/api/species/6/",
"http://swapi.co/api/species/7/",
"http://swapi.co/api/species/3/",
"http://swapi.co/api/species/2/",
"http://swapi.co/api/species/1/"
],
"created": "2014-12-12T11:26:24.656000Z",
"edited": "2015-04-11T09:46:31.433607Z",
"url": "http://swapi.co/api/films/2/"
}
{
"title": "Revenge of the Sith",
"episode_id": 3,
"opening_crawl": "War! The Republic is crumbling\r\nunder attacks by the ruthless\r\nSith Lord, Count Dooku.\r\nThere are heroes on both sides.\r\nEvil is everywhere.\r\n\r\nIn a stunning move, the\r\nfiendish droid leader, General\r\nGrievous, has swept into the\r\nRepublic capital and kidnapped\r\nChancellor Palpatine, leader of\r\nthe Galactic Senate.\r\n\r\nAs the Separatist Droid Army\r\nattempts to flee the besieged\r\ncapital with their valuable\r\nhostage, two Jedi Knights lead a\r\ndesperate mission to rescue the\r\ncaptive Chancellor....",
"director": "George Lucas",
"producer": "Rick McCallum",
"release_date": "2005-05-19",
"characters": [
"http://swapi.co/api/people/1/",
"http://swapi.co/api/people/2/",
"http://swapi.co/api/people/3/",
"http://swapi.co/api/people/4/",
"http://swapi.co/api/people/5/",
"http://swapi.co/api/people/6/",
"http://swapi.co/api/people/7/",
"http://swapi.co/api/people/10/",
"http://swapi.co/api/people/11/",
"http://swapi.co/api/people/12/",
"http://swapi.co/api/people/13/",
"http://swapi.co/api/people/20/",
"http://swapi.co/api/people/21/",
"http://swapi.co/api/people/33/",
"http://swapi.co/api/people/35/",
"http://swapi.co/api/people/46/",
"http://swapi.co/api/people/51/",
"http://swapi.co/api/people/52/",
"http://swapi.co/api/people/53/",
"http://swapi.co/api/people/54/",
"http://swapi.co/api/people/55/",
"http://swapi.co/api/people/56/",
"http://swapi.co/api/people/58/",
"http://swapi.co/api/people/63/",
"http://swapi.co/api/people/64/",
"http://swapi.co/api/people/67/",
"http://swapi.co/api/people/68/",
"http://swapi.co/api/people/75/",
"http://swapi.co/api/people/78/",
"http://swapi.co/api/people/79/",
"http://swapi.co/api/people/80/",
"http://swapi.co/api/people/81/",
"http://swapi.co/api/people/82/",
"http://swapi.co/api/people/83/"
],
"planets": [
"http://swapi.co/api/planets/2/",
"http://swapi.co/api/planets/5/",
"http://swapi.co/api/planets/8/",
"http://swapi.co/api/planets/9/",
"http://swapi.co/api/planets/12/",
"http://swapi.co/api/planets/13/",
"http://swapi.co/api/planets/14/",
"http://swapi.co/api/planets/15/",
"http://swapi.co/api/planets/16/",
"http://swapi.co/api/planets/17/",
"http://swapi.co/api/planets/18/",
"http://swapi.co/api/planets/19/",
"http://swapi.co/api/planets/1/"
],
"starships": [
"http://swapi.co/api/starships/48/",
"http://swapi.co/api/starships/59/",
"http://swapi.co/api/starships/61/",
"http://swapi.co/api/starships/32/",
"http://swapi.co/api/starships/63/",
"http://swapi.co/api/starships/64/",
"http://swapi.co/api/starships/65/",
"http://swapi.co/api/starships/66/",
"http://swapi.co/api/starships/68/",
"http://swapi.co/api/starships/74/",
"http://swapi.co/api/starships/75/",
"http://swapi.co/api/starships/2/"
],
"vehicles": [
"http://swapi.co/api/vehicles/33/",
"http://swapi.co/api/vehicles/50/",
"http://swapi.co/api/vehicles/60/",
"http://swapi.co/api/vehicles/62/",
"http://swapi.co/api/vehicles/67/",
"http://swapi.co/api/vehicles/69/",
"http://swapi.co/api/vehicles/70/",
"http://swapi.co/api/vehicles/71/",
"http://swapi.co/api/vehicles/72/",
"http://swapi.co/api/vehicles/73/",
"http://swapi.co/api/vehicles/76/",
"http://swapi.co/api/vehicles/53/",
"http://swapi.co/api/vehicles/56/"
],
"species": [
"http://swapi.co/api/species/6/",
"http://swapi.co/api/species/15/",
"http://swapi.co/api/species/19/",
"http://swapi.co/api/species/20/",
"http://swapi.co/api/species/23/",
"http://swapi.co/api/species/24/",
"http://swapi.co/api/species/25/",
"http://swapi.co/api/species/26/",
"http://swapi.co/api/species/27/",
"http://swapi.co/api/species/28/",
"http://swapi.co/api/species/29/",
"http://swapi.co/api/species/30/",
"http://swapi.co/api/species/33/",
"http://swapi.co/api/species/34/",
"http://swapi.co/api/species/35/",
"http://swapi.co/api/species/36/",
"http://swapi.co/api/species/37/",
"http://swapi.co/api/species/3/",
"http://swapi.co/api/species/2/",
"http://swapi.co/api/species/1/"
],
"created": "2014-12-20T18:49:38.403000Z",
"edited": "2015-04-11T09:45:44.862122Z",
"url": "http://swapi.co/api/films/6/"
}
{
"title": "The Force Awakens",
"episode_id": 7,
"opening_crawl": "Luke Skywalker has vanished.\r\nIn his absence, the sinister\r\nFIRST ORDER has risen from\r\nthe ashes of the Empire\r\nand will not rest until\r\nSkywalker, the last Jedi,\r\nhas been destroyed.\r\n \r\nWith the support of the\r\nREPUBLIC, General Leia Organa\r\nleads a brave RESISTANCE.\r\nShe is desperate to find her\r\nbrother Luke and gain his\r\nhelp in restoring peace and\r\njustice to the galaxy.\r\n \r\nLeia has sent her most daring\r\npilot on a secret mission\r\nto Jakku, where an old ally\r\nhas discovered a clue to\r\nLuke's whereabouts....",
"director": "J. J. Abrams",
"producer": "Kathleen Kennedy, J. J. Abrams, Bryan Burk",
"release_date": "2015-12-11",
"characters": [
"http://swapi.co/api/people/1/",
"http://swapi.co/api/people/3/",
"http://swapi.co/api/people/5/",
"http://swapi.co/api/people/13/",
"http://swapi.co/api/people/14/",
"http://swapi.co/api/people/27/",
"http://swapi.co/api/people/84/",
"http://swapi.co/api/people/85/",
"http://swapi.co/api/people/86/",
"http://swapi.co/api/people/87/",
"http://swapi.co/api/people/88/"
],
"planets": [
"http://swapi.co/api/planets/61/"
],
"starships": [
"http://swapi.co/api/starships/77/",
"http://swapi.co/api/starships/10/"
],
"vehicles": [],
"species": [
"http://swapi.co/api/species/1/",
"http://swapi.co/api/species/2/",
"http://swapi.co/api/species/3/"
],
"created": "2015-04-17T06:51:30.504780Z",
"edited": "2015-12-17T14:31:47.617768Z",
"url": "http://swapi.co/api/films/7/"
}
{
"title": "Return of the Jedi",
"episode_id": 6,
"opening_crawl": "Luke Skywalker has returned to\r\nhis home planet of Tatooine in\r\nan attempt to rescue his\r\nfriend Han Solo from the\r\nclutches of the vile gangster\r\nJabba the Hutt.\r\n\r\nLittle does Luke know that the\r\nGALACTIC EMPIRE has secretly\r\nbegun construction on a new\r\narmored space station even\r\nmore powerful than the first\r\ndreaded Death Star.\r\n\r\nWhen completed, this ultimate\r\nweapon will spell certain doom\r\nfor the small band of rebels\r\nstruggling to restore freedom\r\nto the galaxy...",
"director": "Richard Marquand",
"producer": "Howard G. Kazanjian, George Lucas, Rick McCallum",
"release_date": "1983-05-25",
"characters": [
"http://swapi.co/api/people/1/",
"http://swapi.co/api/people/2/",
"http://swapi.co/api/people/3/",
"http://swapi.co/api/people/4/",
"http://swapi.co/api/people/5/",
"http://swapi.co/api/people/10/",
"http://swapi.co/api/people/13/",
"http://swapi.co/api/people/14/",
"http://swapi.co/api/people/45/",
"http://swapi.co/api/people/16/",
"http://swapi.co/api/people/18/",
"http://swapi.co/api/people/20/",
"http://swapi.co/api/people/21/",
"http://swapi.co/api/people/22/",
"http://swapi.co/api/people/25/",
"http://swapi.co/api/people/27/",
"http://swapi.co/api/people/28/",
"http://swapi.co/api/people/29/",
"http://swapi.co/api/people/30/",
"http://swapi.co/api/people/31/"
],
"planets": [
"http://swapi.co/api/planets/5/",
"http://swapi.co/api/planets/7/",
"http://swapi.co/api/planets/8/",
"http://swapi.co/api/planets/9/",
"http://swapi.co/api/planets/1/"
],
"starships": [
"http://swapi.co/api/starships/10/",
"http://swapi.co/api/starships/11/",
"http://swapi.co/api/starships/12/",
"http://swapi.co/api/starships/15/",
"http://swapi.co/api/starships/22/",
"http://swapi.co/api/starships/23/",
"http://swapi.co/api/starships/27/",
"http://swapi.co/api/starships/28/",
"http://swapi.co/api/starships/29/",
"http://swapi.co/api/starships/3/",
"http://swapi.co/api/starships/17/",
"http://swapi.co/api/starships/2/"
],
"vehicles": [
"http://swapi.co/api/vehicles/8/",
"http://swapi.co/api/vehicles/16/",
"http://swapi.co/api/vehicles/18/",
"http://swapi.co/api/vehicles/19/",
"http://swapi.co/api/vehicles/24/",
"http://swapi.co/api/vehicles/25/",
"http://swapi.co/api/vehicles/26/",
"http://swapi.co/api/vehicles/30/"
],
"species": [
"http://swapi.co/api/species/5/",
"http://swapi.co/api/species/6/",
"http://swapi.co/api/species/8/",
"http://swapi.co/api/species/9/",
"http://swapi.co/api/species/10/",
"http://swapi.co/api/species/15/",
"http://swapi.co/api/species/3/",
"http://swapi.co/api/species/2/",
"http://swapi.co/api/species/1/"
],
"created": "2014-12-18T10:39:33.255000Z",
"edited": "2015-04-11T09:46:05.220365Z",
"url": "http://swapi.co/api/films/3/"
}
Alright, let's fix this
http://swapi.co/api/people/1?
with=homeworld.name,film.name
http://swapi.co/api/people-with-homeworld-and-films
http://swapi.co/api/people-with-association-names
Endpoints galore
Attempting to circumvent the aforementioned problems often leads to a huge amount of bespoke/ad-hoc endpoints. This, in turn, inevitably increases the complexity of your application.
So far, we haven't even talked about versioning and backwards compatibility!
Wouldn't it be great if ...
What is this sorcery?
Tokenizer and parser for the GraphQL language specifiction
A Data querying language ...
Running on arbitrary code ...
Backed by a schema ...
Based on a type system ...
Making it fully introspective ...
Not a query language for a graph database.
It runs on arbitrary code.
It is completely agnostic of your storage layer and can potentially support any backend architecture.
Evolving the server–client relationship
The server publishes its possibilities; the client specifies its requirements.
Learning GraphQL
For the adventurous:
http://graphql-swapi.parseapp.com/
For the tourists:
https://learngraphql.com/
For the brave:
https://facebook.github.io/graphql/
Let's see it in action
Queries
Mutations
Fragments
Directives
Variables
Arguments
Sub selections
Aliasing
Subscriptions
Schema
Each schema is an arbitrarily nested hierarchy of type definitions.
Introspection
Exposing your own schema through the type system
Do you remember the movie "Inception"?
Time for a demo
Building a GraphQL Server
GraphQL
Your application code
Database
Internal APIs
Remote APIs
What about Drupal?
Exposing your entire Drupal data graph through a fully generated, self-documenting schema.
The goal
Make the resulting graph accessible through single and batch loading of entities using the EntityQuery API.
The goal
TypedData API
We've got the ideal foundation
... Translate TypedData definitions into GraphQL Types and compose them in a GraphQL Schema.
The module's job is to ...
Initial version of the module has been released recently.
Time for a demo
Limitations
Not Relay compliant
No Mutations
No Config Entities
Outlook
Relay compliance
Mutation support
Config entity support
Schema customization
Query routes and obfuscation
Bonus
Questions
Resources
- RFC Working Draft: https://facebook.github.io/graphql/
- Reference implemenetation: https://github.com/graphql/graphql-js
- GraphiQL: https://github.com/graphql/graphiql
- StarWars API Playground: http://graphql-swapi.parseapp.com/
- Relay: https://facebook.github.io/relay/docs/graphql-relay-specification.html
- Learn GraphQL: https://learngraphql.com/
- GraphQL & Relay Drupal Demo: https://github.com/fubhy/drupal-decoupled-app
GraphQL Meets Drupal - DrupalCamp CS 2016
By Sebastian Siemssen
GraphQL Meets Drupal - DrupalCamp CS 2016
- 1,569