1Ghent University – imec – IDLab, Belgium
2Department of Computer and Information Science (IDA) — Linköping University, Sweden
In:
{ me { name } }
Out:
{ "data": { "me": { "name": "Luke Skywalker" } } }
describes structure of data that can be queried
retrieves data for specific fields
executes full GraphQL queries
Query
type defines all query entrypoints.type Query { me: User } type User { id: ID name: String }
function(parent, args, context, info)
parent
: Resolver result from parent field, or root value.args
: Arguments passed to the field. (e.g. { key: "value" }
)context
: Object shared by resolvers per request (e.g. authentication)info
: Query execution state (e.g. field name, path)const hero = (_, args) => db.find('hero', { name: args.name });
GraphQL Query:
query { hero { name friends { name } } }
AST:
GraphQL Schema:
type Query { hero: Character } type Character { name: String! friends: [Character] }
Validation steps:
hero
in Query
?name
in Character
?friends
in Character
?name
in Character
?AST:
Resolvers:
{ Query: { hero: () => db.findAll('hero'), }, Character: { name: (char) => db.find('name', char), friends: (char) => db.findAll('friend', char), } }
AST:
Resolvers:
{ Query: { hero: () => db.findAll('hero'), }, Character: { name: (char) => db.find('name', char), friends: (char) => db.findAll('friend', char), } }
AST:
→ [1,2]
Resolvers:
{ Query: { hero: () => db.findAll('hero'), }, Character: { name: (char) => db.find('name', char), friends: (char) => db.findAll('friend', char), } }
AST:
→ [{name:'Luke',friends:[2]},{name:'Leia',friends:[1]}]
Resolvers:
{ Query: { hero: () => db.findAll('hero'), }, Character: { name: (char) => db.find('name', char), friends: (char) => db.findAll('friend', char), } }
AST:
→ [{name:'Luke',friends:[...]},{name:'Leia',friends:[...]}]
It includes:
It does not include:
Schema and resolvers are combined in a single datastructure.
const schema = new GraphQLSchema({ query: new GraphQLObjectType({ name: 'RootQueryType', fields: { hello: { type: GraphQLString, resolve() { return 'world'; } } } }) }); const result = await graphql(schema, '{ hello }');
express-graphql
Express example:
app.use('/graphql', graphqlHTTP({ schema: MyGraphQLSchema, graphiql: true }));
Main features:
(+): Team plan; (++): Enterprise plan
const typeDefs = gql` type Book { title: String author: String } type Query { books: [Book] } `; const resolvers = { Query: { books: () => ..., }, }; const server = new ApolloServer({ typeDefs, resolvers }); server.listen().then(({ url }) => { console.log(`🚀 Server ready at ${url}`); });
data sources, caching, authentication, subscriptions, testing, ...
Data sources are abstractions for data fetching from specific services.
Built-in support for caching, result deduplication, and error handling.
class MoviesAPI extends RESTDataSource { constructor() { super(); this.baseURL = 'https://movies-api.example.com/'; } async getMovie(id) { return this.get(`movies/${id}`); } }
Data sources must be configured in server.
Resolvers can access data sources from context.
const server = new ApolloServer({ ... dataSources: () => { return { moviesAPI: new MoviesAPI() }; }, }); const resolvers = { Query: { movie: async (source, { id }, { dataSources }) => { return dataSources.moviesAPI.getMovie(id); }, }, };
Resources are by default cached with an in-memory LRU cache.
Alternative caches can be configured, such as Memcached or Redis
const { MemcachedCache } = require('apollo-server-cache-memcached'); const server = new ApolloServer({ ... cache: new MemcachedCache( ['memcached-server-1', 'memcached-server-2'], { retries: 10, retry: 10000 }, // Options ), });
HTTP authorization headers can be placed into the context.
const server = new ApolloServer({ ... context: ({ req }) => { const token = req.headers.authorization || ''; const user = getUser(token); return { user }; }, });
Different publish and subscribe primitives: PubSub, WebSocket
const typeDefs = gql` type Subscription { postAdded: Post } type Post { author: String comment: String }` const resolvers = { Subscription: { postAdded: { subscribe: () => pubsub.asyncIterator(['POST_ADDED']), // emit with pubsub.publish('POST_ADDED', { postAdded:args }); }, }, };
Mocks the request pipeline and HTTP server
Allows testing of schema, resolvers and datasources
it('fetches single launch', async () => { const { query } = createTestClient(server); const res = await query({ query: '...', variables: { id: 1 } }); expect(res).toMatchSnapshot(); }
Apollo has
(+): Team plan; (++): Enterprise plan
Apollo is the most popular platform, used by Airbnb, Twitch, Medium, ...
It accepts GraphQL queries and returns JSON results
GraphQL schema, Resolvers, and a Query Engine
It enables GraphQL querying in code (graphql-express
for HTTP interface)
Commercial tools such as Apollo, Yoga, ...