52
Better APIs with GraphQL Josh Price github.com/joshprice @joshprice

Better APIs with GraphQL

Embed Size (px)

Citation preview

Page 1: Better APIs with GraphQL

Better APIswith GraphQL

Josh Pricegithub.com/joshprice

@joshprice

Page 2: Better APIs with GraphQL

Agenda• Understand GraphQL and why you'd use it

• Build a simple schema

• Run queries against the schema

• Understand important GraphQL concepts

• Summarise client options

Page 3: Better APIs with GraphQL

REST APIs

Page 4: Better APIs with GraphQL

REST is great

Page 5: Better APIs with GraphQL

REST is hard

Page 6: Better APIs with GraphQL

Cargo Cults

Page 7: Better APIs with GraphQL

Common ProblemsOverfetchingUnderfetching

Page 8: Better APIs with GraphQL

Internal APIs havestrong contracts

betweenclient and server

Page 9: Better APIs with GraphQL

GraphQL

Page 10: Better APIs with GraphQL

What is GraphQL?• Language for defining schemas, types & queries

• Developed by Facebook in 2012

• Used to improve mobile app performance

• Serves 300 billion+ requests per day

Page 11: Better APIs with GraphQL

Open Source• Open sourced in July 2015

• Specification

• facebook.github.io/graphql

• Reference Implementation

• github.com/graphql/graphql-js

• Relay released in August 2015

Page 12: Better APIs with GraphQL

GraphQL Server Implementations• JavaScript reference

• Ruby / Python / PHP

• Java / Scala (Sangria)

• .NET

• Elixir / Go / Haskell / OCaml

• etc...

Page 13: Better APIs with GraphQL

GraphQL Misconceptions• Not really about "graphs"

• A specification for client/server interaction

• Language independent

• Assumes nothing about:

• transport

• message protocol

• data store

Page 14: Better APIs with GraphQL

Exhibit A: REST APIFetch user name and friends names with 2 requestsGET /users/1GET /users/1/friends

or a single requestGET /users/1/friends?include=user.name,friend.name

Page 15: Better APIs with GraphQL

Exhibit B: GraphQL API{ "data": { user(id: 1) { "user": { name "name": "Josh", friends(first: 1) { "friends": [{ name "name": "James" } }] } } } }

Page 16: Better APIs with GraphQL

BetterMentalModel

Page 17: Better APIs with GraphQL

Stronglytyped

Page 18: Better APIs with GraphQL

SingleEndpoint

Page 19: Better APIs with GraphQL

SingleUnambiguous

Query

Page 20: Better APIs with GraphQL

ConsumerDriven

Contracts

Page 21: Better APIs with GraphQL

LessVersioning

Page 22: Better APIs with GraphQL

SelfDocumenting

Page 23: Better APIs with GraphQL

Performance

Page 24: Better APIs with GraphQL

Let's build ourfirst Schema

Page 25: Better APIs with GraphQL

Query and Mutation Rootstype Query { me: User user(id: Int): User}

type Mutation { createPost(title: String!): Post createComment(message: String!): Comment}

Page 26: Better APIs with GraphQL

Object Types and Enumstype User { name: String profilePicture(size: Int = 50): ProfilePicture friends(first: Int, orderBy: FriendOrder): [User]}

enum FriendOrder { FIRST_NAME, LAST_NAME, IMPORTANCE }

Page 27: Better APIs with GraphQL

More Typestype ProfilePicture { width: Int height: Int url: String}

type Event { name: String attendees(first: Int): [User]}

Page 28: Better APIs with GraphQL

Simplest Query{ me { name } }

{ "data": { "me": { "name": "Josh Price" } }}

Page 29: Better APIs with GraphQL

Deeply Nested Query{ { me { "data": { name "name": "Josh Price", profilePicture { "profilePicture": { url "url": "http://cdn/josh_50x50.png" } }, friends(first: 1) { "friends": [{ name "name": "James Sadler" } }], events(first: 1) { "events": [{ name "name": "Afterparty!", attendees(first: 1) { "attendees": [{ name "name": "Jenny Savage" } }] } }] } } } }

Page 30: Better APIs with GraphQL

How do we fetch data?

Page 31: Better APIs with GraphQL

Resolvers• Your own functions

• Could use in memory data

• Call any data store

• Proxy REST APIs

• Call existing services

• GraphQL doesn't care

• Resolver "Middleware" is possible (Auth, Logging, etc)

Page 32: Better APIs with GraphQL

User Type with JS Resolversnew GraphQLObject({ type: "User", fields: { name(user) { return user.name }, profilePicture(user, {size}) { return getProfilePicForUser(user, size); }, friends(user) { return user.friendIDs.map(id => getUser(id)); } }});

Page 33: Better APIs with GraphQL

Mutations Modify Datamutation { acceptFriendRequest(userId: 345124) { user { friends { count } } }}

mutation { rsvpToEvent(eventId: 134624, status: ATTENDING) { event { invitees { count } attendees { count } } }}

Page 34: Better APIs with GraphQL

Setup GraphQL Expressimport { Schema } from './schema.js';import graphqlHTTP from 'express-graphql';import express from 'express';

const app = express();

app.get('/', function(req, res) { res.redirect('/graphql');});

app.use('/graphql', graphqlHTTP({ schema: Schema, graphiql: true }));

app.listen(3000);

Page 35: Better APIs with GraphQL

Relay• Each view component declares query fragment

• Relay batches all data req'ts for render tree

• Sends single query

• Handles caching using global IDs

• Relies on schema conventions for metadata

Page 36: Better APIs with GraphQL

Client-Side Alternatives• ApolloStack Client

• React + Native

• Angular 2

• Redux support

• Lokka

• Simple

Page 37: Better APIs with GraphQL

Gotchas• Arbitrary Queries

• Could be slow if deeply nested (friends of friends...)

• Complexity analysis

• Query depth

• Batching resolvers

• Data Loader (JS)

• GraphQL Batch (Ruby)

Page 38: Better APIs with GraphQL

When to use?• Use for internal APIs first, or limited external use

• Improve mobile (and desktop performance)

• Github has exposed their API externally

• Be careful exposing this to the world!

• Don't allow arbitrary queries from unknown clients

Page 39: Better APIs with GraphQL

GraphQL EcosystemEvolving Quickly

Page 40: Better APIs with GraphQL

GraphQL Backend as a Service• reindex.io

• graph.cool

• scaphold.io

Page 41: Better APIs with GraphQL

Future - GraphQL Spec• Push: Apps should reflect current state of world

• Subscriptions + Reactive Backend + RethinkDB

• Defer

• Stream

• Live queries

• GraphQL CATS

Page 42: Better APIs with GraphQL

Subscriptionssubscription { createCommentSubscribe(storyId: $id) { comment { ...FBCommentFragment } }}

Page 43: Better APIs with GraphQL

Warning!Experimental

Page 44: Better APIs with GraphQL

Defer Directive{ { feed { "feed": { stories { "stories": [{ author { name } "author": { "name": "Lee Byron" }, title "title": "GraphQL is the Future" comments @defer { }, { author { name } "author": { "name": "Josh Price" }, comment "title": "REST is old school" } }] } } } } }

Page 45: Better APIs with GraphQL

Defer - Comments arrive{ { feed { "path": ["feed", "stories", 0, "comment"], stories { "data": [{ author { name } "author": { "name": "Joe Bloggs" }, title "comment": "That blew my mind!" comments @defer { }, { author { name } "author": { "name": "Jenny Savage" }, comment "comment": "I love it" } }] } } } }

Page 46: Better APIs with GraphQL

Stream Directive{ { feed { "feed": { stories @stream { "stories": [] author { name } } title } comments @defer { author { name } comment } } } }

Page 47: Better APIs with GraphQL

Stream - First Story{ { feed { "path": ["feed", "stories", 0], stories @stream { "data": [{ author { name } "author": { "name": "Joe Bloggs" }, title "title": "That blew my mind!" comments @defer { }] author { name } } comment } } } }

Page 48: Better APIs with GraphQL

Live Directive{ { feed { "feed": { stories { "stories": [{ author { name } "author": { "name": "Lee Byron" }, title "title": "GraphQL is the Future", likeCount @live "likeCount": 9 } }] } }} }

Page 49: Better APIs with GraphQL

Live - Likes update on backend{ { feed { "path": ["feed", "stories", 0, "likeCount"], stories { "data": 10 author { name } } title likeCount @live } } }

Page 50: Better APIs with GraphQL

Resources• graphql.org

• Github

• graphql/graphql-js

• graphql/express-graphql

• Steve Luscher talk Zero to GraphQL

• Awesome GraphQL (chentsulin/awesome-graphql)

Page 51: Better APIs with GraphQL

Questions?

Page 52: Better APIs with GraphQL

Thanks!