GraphQL to the Rescue



Presented by @jayhogan
Application Development Lead, SingleStone Consulting




Agenda

  1. GraphQL quickly defined
  2. Why does it exist?
  3. Deep dive
  4. Why should you use it?
  5. Challenges with GraphQL
  6. Obligatory code walk-through

GraphQL quickly defined

A query language for your API

A type system to describe your data

A server-side runtime to execute queries

Why build something new?

Challenges with REST


GET http://myapi.io/author/2
HTTP/1.1 200 OK
{
	"first_name":"Douglas",
	"last_name":"Crockford"
}

GET http://myapi.io/author/2/books
HTTP/1.1 200 OK
[{
	"id":3,
	"name":"Javascript: The Good Parts",
	"isbn":"0596517742",
	"publish_date":"2016-05-31"
}]
						

Multiple requests to server required


							GET http://myapi.io/books

							# For each book - yikes!
							GET http://myapi.io/book/1
							
							GET http://myapi.io/book/1/ratings
							
							GET http://myapi.io/author/2
							
							GET http://myapi.io/author/2/books
						

Response payloads grow over time

							
							GET http://myapi.io/book/3?view=full
							{
								"id":3,
								"name":"Javascript: The Good Parts",
								"isbn":"0596517742",
								"publish_date":"2016-05-31",
								"author": {
									"first_name":"Douglas",
									"last_name":"Crockford"
								}
							}

							
							GET http://myapi.io/book/3?view=compact
							{
								"id":3,
								"name":"Javascript: The Good Parts"
							}
							
						

Weakly typed


							GET http://myapi.io/book/A
							HTTP/1.1 500
							Unexpected error


							POST http://myapi.io/book/1
							{"foo":"bar"}
							HTTP/1.1 500
							Unexpected error
						

REST is not a good fit


"REST is intended for long-lived network-based applications that span multiple organizations." -- Roy Fielding

"What needs to be done to make the REST architectural style clear on the notion that hypertext is a constraint?" -- Roy Fielding

What about Ad Hoc APIs?

Custom end point per view


							GET http://myapi.io/book

							GET http://myapi.io/bookComparison
							
							GET http://myapi.io/bookSnippet

							GET http://myapi.io/bookMobile
						

Even simple front end changes often require changes to the server

Product Owner: Can I add the book publication date to the page?

Developer: That's a 3 point story.

Product Owner: But it's just a simple date!

GraphQL Deep Dive

Query language


							{
								books {
									name
									author {
										first_name
										last_name
									}
									publish_date
								}
							}
						

							"books": [
								{
									"name": "JavaScript: The Definitive Guide",
									"author": {
										"first_name": "David",
										"last_name": "Flanagan"
									},
									"publish_date": "2011-05-13"
								},
								{
									"name": "JavaScript: Pocket Reference",
									"author": {
										"first_name": "David",
										"last_name": "Flanagan"
									},
									"publish_date": "2012-04-28"
								}]
						

Arguments


							{
								books {
									name
									price(currency: USD)
								}
							}
						

							"books": [
								{
									"name": "JavaScript: The Definitive Guide",
									"price": 32.64
								},
								{
									"name": "JavaScript: Pocket Reference",
									"price": 13.47
								}]
						

Aliases


							{
								books {
									name
									price(currency: USD)
									euPrice: price(currency: EUR)
								}
							}
						

							"books": [
								{
									"name": "JavaScript: The Definitive Guide",
									"price": 32.64,
									"euPrice": 28.0704
								},
								{
									"name": "JavaScript: Pocket Reference",
									"price": 13.47,
									"euPrice": 11.584200000000001
								}]
						

Variables


							{
								books {
									name
									price(currency: $currency)
								}
							}

							# Variables
							{
								currency: "USD"
							}
						

							"books": [
								{
									"name": "JavaScript: The Definitive Guide",
									"price": 32.64
								},
								{
									"name": "JavaScript: Pocket Reference",
									"price": 13.47
								}]
						

Mutations


							mutation AddRating($rating: RatingInput!) {

								addRating(rating: $rating) {
									id
									rating
									date
								}

							}

							# Variables
							{
								rating {
									book_id: 1, user_id: 1, rating: 5
								}
							}
						

							"addRating": {
								"id": 15,
								"rating": 5,
								"date": "2017-11-01"
							}
						

And more!

But there is no more time...sorry. ¯\_(ツ)_/¯

Type system


							type Book {
								id: Int
								name: String
								isbn: String
							}
						

Scalar types


							type ScalarGrabBag {
								id: ID
								string: String
								float: Float
								int: Int
								bool: Boolean
							}
						

Composable, hierarchical object types


							type Book {
								id: Int
								name: String
								isbn: String
								author: Author
							}

							type Author {
								first_name: String
								last_name: String
							}
						

Type modifiers


							type Book {
								id: Int! # id will never be null
								name: String
								isbn: String
								author: Author!
								ratings: [Rating] # ratings is a list/array
							}
						

Enumerations


							enum Currency {
								USD
								CAD
								EUR
							}
						

Arguments


							type Book {
								id: Int!
								name: String
								isbn: String
								price(currency: Currency = USD): Float!
							}
						

Query and mutation types


							type Query {
								books: [Book]
								authors: [Author]
								users: [User]
								search(name: String): [SearchResult]
							}
						
							type Mutation {
								addRating(rating: RatingInput!): Rating
							}
						

And more!

But there is no more time...sorry. ¯\_(ツ)_/¯

Services

Server-side validation and execution of graphql queries

API developers leverage GraphQL server libraries

Use the library to build the type system and field resolvers

Step 1: Validation

Comes for free with compliant GraphQL server libraries

Step 2: Execution


								{
									books {           () => repo.books()  # returns array of books
																		# for each book
										name              (book) => book.name

										author {          (book) => repo.findAuthor(book)  # returns author object

											first_name      (author) => author.first_name

										}
									}
								}
							

Rescue Me!

REST APIs

Multiple requests to server required


							{
								books {
									name
									author {
										first_name
										last_name
										bio
										books { name }
									}
									ratings { rating }
								}
							}
							

								{ "books": [
									{
										"name": "JavaScript: The...",
										"author": {
											"first_name": "David",
											"last_name": "Flanagan",
											"bio": "David Flanagan is a computer programmer who..."
											"books": [
												{ "name": "JavaScript: T..." }
											]
										},
										"ratings": [
											{ "rating": 3 }
										]
									}
								]}
							

Payloads grow over time


								{
									books {
										name
										isbn
										author {
											first_name
											last_name
											books { name }
										}
										price(currency: USD)
									}
								}
							

								{
									books {
										name
										author {
											last_name
										}
									}
								}
							

Weakly typed


							{
								books {
									name
									author { firstName }
								}
							}

							# Response
							{
								error: "Cannot find field 'firstName' on 'Author'. Did you mean 'first_name'?"
							}
						

REST is not a good fit

Designed for building client-focused, product-based APIs

Ad hoc APIs

Custom end point per view

Now there is one endpoint: /graphql

Even simple front end changes often require changes to the API

Not with GraphQL


							{
								books {
									name
									author { firstName }
									publish_date
								}
							}
						

What are some challenges?

Lack of best practices

Breaks HTTP Caching

Overly complex queries


							{
								books {
									name
									author {
										books {
											author {
												books {
													author {
														last_name
													}
												}
											}
										}
									}
								}
							}
						

n+1 query problem


							{
								books {
									name
									author { # Resolver is called for each book
										books { # Resolver is called for each book, author
											name
										}
									}
								}
							}
						

Show me the CODE!

Resources

Background & Documentation

singlestone/graphql-rescue » GraphQL » Docs » Community Resources » GraphQL Spec » Explore GraphQL » Fullstack Tutorial

Server Libraries

GraphQL.js » Express » Apollo » GraphQL for .NET » GraphQL for Java » and more

Client Libraries

Apollo React » Apollo Angular » Apollo iOS » Apollo Android » and more

Tools

Apollo Launchpad » Apollo Optics » GraphiQL

Hosted GraphQL (BaaS)

Graphcool » Scaphold

Thank You