title: GraphQL author: name: George Czabania twitter: stayradiated style: style.css
--
--
- Intro: What is GraphQL and why it is so awesome
- Demo: Querying a GraphQL server
- Tutorial: How to setup a GraphQL server from scratch
- Challenges: Things to consider
--
--
--
GraphQL = Graph Query Language.
Built by Facebook several years ago, released as open source last year.
It's a spec, and it's platform agnostic.
--
RESTful web API's can be pretty good.
GET /users/123
POST /users
--
When a client needs to make multiple requests to get all the data it needs.
GET /users/123
GET /users/123/history
GET /users/123/recommended
- ...
--
--
We could include extra information into a response.
Which would cut down the number of API calls we have to make.
But increases the size of the response.
GET /users/123
--
--
We could add an option to embed the values we care about in the API.
GET /videos/123?embed=history,recommended
Then only the clients that need that info can request it
GraphQL takes this idea and builds a language out of it.
--
--
--
--
--
QUERY RESPONSE
===== ========
{ {
user(id: 43) { "user": {
name "name": "John Smith",
avatar { "avatar": {
small "small": "http://...",
} }
history { "history": [{
title "title": "Video 1",
date "date": "2016-10-25",
image { "image": {
large "large": "http://...",
} }
} }]
} }
} }
--
--
Get All Users
http http://localhost:5000/graphql query='{users {username}}'
Get All Users & Include Videos
http http://localhost:5000/graphql query='{users {username videos {title}}}'
--
--
query {
users {
username
videos {
title
}
}
}
--
mutation {
createUser(name: "George") {
id
username
}
}
--
Try to avoid inlining variables.
const query = `
{
user(id: "${userID}") {
id
videos {
title
}
}
}
`
Instead, use variables.
const query = `
query getUserVideos($userID: Int!) {
user(id: $userID) {
id
videos {
title
}
}
}
`
--
--
-
store.getUser(id)
-
store.getUsers([id])
-
store.getAllUsers()
-
store.getVideo(id)
-
store.getVideos([id])
-
store.getAllVideos()
-
store.createUser()
--
User
{
"id": 1,
"username": "Thomas.Pollich",
"videos": [ 11, 23, 30 ],
"followers": [ 5 ]
}
Video
{
"id": "49",
"user": 5,
"title": "A Slight Case of August",
"source": {
"low": "https://75.146.219.97/videos/49.360.mp4",
"med": "https://75.146.219.97/videos/49.720.mp4",
"high": "https://75.146.219.97/videos/49.1080.mp4"
}
}
--
type User {
id: Int
username: String
videos: [Video]
followers: [User]
}
type Video {
id: Int
title: String
user: User
source: VideoSource
}
type VideoSource {
low: String
med: String
high: String
}
--
All the GraphQL variables are from the graphql
package.
const {
GraphQLInt,
GraphQLString,
GraphQLObjectType,
...
} = require('graphql')
--
const VideoType = new GraphQLObjectType({
name: 'Video',
description: 'Information about a single video',
fields: () => ({
id: { type: GraphQLInt },
title: { type: GraphQLString },
user: { type: UserType },
source: { type: VideoSourceType },
}),
})
const VideoSourceType = new GraphQLObjectType({
name: 'VideoSource',
description: 'URLs of varying quality for a video file',
fields: {
low: { type: GraphQLString },
med: { type: GraphQLString },
high: { type: GraphQLString },
},
})
const UserType = new GraphQLObjectType({
name: 'User',
description: 'Information about a single user',
fields: () => ({
id: { type: GraphQLInt },
username: { type: GraphQLString },
videos: { type: new GraphQLList(VideoType) },
followers: { type: new GraphQLList(UserType) },
})
--
Videos
const VideoType = new GraphQLObjectType({
name: 'Video',
fields: () => ({
id: {type: GraphQLInt},
title: {type: GraphQLString},
user: {
type: UserType,
resolve: (video) => store.getUser(video.user),
},
source: {type: VideoSourceType},
}),
})
Users
const UserType = new GraphQLObjectType({
name: 'User',
fields: () => ({
id: {type: GraphQLInt},
username: {type: GraphQLString},
videos: {
type: new GraphQLList(VideoType),
resolve: (user) => store.getVideos(user.videos),
},
followers: {
type: new GraphQLList(UserType),
resolve: (user) => store.getUsers(user.followers),
},
}),
})
--
const QueryType = new GraphQLObjectType({
name: 'Query',
fields: {
users: {
type: new GraphQLList(UserType),
resolve: () => store.getAllUsers(),
},
user: {
type: UserType,
args: {
id: { type: new GraphQLNonNull(GraphQLInt) }
},
resolve: (_, args) => store.getUser(args.id)
},
},
})
--
const MutationType = new GraphQLObjectType({
name: 'Mutaton',
fields: {
createUser: {
type: UserType,
args: {
name: { type: new GraphQLNonNull(GraphQLString) },
},
resolve: (_, args) => store.createUser(args.name)
},
},
})
--
The query
field is required.
const schema = new GraphQLSchema({
query: QueryType,
mutation: MutationType,
})
--
const {graphql} = require('graphql')
const query = `
query {
user(id: 1) {
username
videos {
title
}
}
}
`
graphql(schema, query)
.then((value) => console.log(value))
Result
{
"data": {
"user": {
"username": "Jarret_Schmeler",
"videos": [
{
"title": "Ruggles of the King George"
},
{
"title": "The Dreamlife of Apu"
},
{
"title": "House of Endearment"
}
]
}
}
}
--
const query = `
mutation createNewUser {
createUser(name: "Jeremy") {
id
username
videos {
title
}
}
}
`
graphql(schema, query)
.then((value) => console.log(value))
Result
{
"data": {
"createUser": {
"id": 15,
"username": "Jeremy",
"videos": []
}
}
}
--
const Express = require('express')
const bodyParser = require('body-parser')
const {graphqlExpress, graphiqlExpress} = require('graphql-server-express')
const app = new Express()
app.use('/graphql',
bodyParser.json(),
graphqlExpress({ schema }))
app.use('/graphiql',
graphiqlExpress({ endpointURL: '/graphql' }))
app.listen(5000, () => console.log('Listening on port 5000'))
--
const typeDefs = `
schema {
query: Query
mutation: Mutation
}
type Query {
user(id: Int!): User
users: [User]
}
type Mutation {
createUser(name: String!): User
}
type User {
id: Int
username: String
videos: [Video]
followers: [User]
}
type Video {
id: Int
title: String
user: User
source: VideoSource
}
type VideoSource {
low: String
med: String
high: String
}
`
const resolvers = {
Query: {
user: (_, {id}) => store.getUser(id),
users: () => store.getAllUsers(),
},
Mutation: {
createUser: (_, {name}) => store.createUser(name),
},
User: {
videos: (user) => store.getVideos(user.videos),
followers: (user) => store.getUsers(user.followers),
},
Video: {
user: (video) => store.getUser(video.user),
},
}
const {makeExecutableSchema} = require('graphql-tools')
const schema = makeExecutableSchema({typeDefs, resolvers})
--
--
- Sometimes need to return a lot of data
- Batching + Caching
- Facebook Data Loader
--
- Can't restrict based on endpoint
- Can check user permissions when resolving each part of the query
--
- Deeply nested queries use up the server capacity
- Facebook: persisted approved queries
- Timeout on computation time
- Complexity analysis