Building GraphQL Resolvers
Figuring out how to make GraphQL resolvers is tricky when you get past the basic static routes. Here's a straight forward tutorial that goes over creating resolvers.
Prerequisite
The previous tutorials cover what GraphQL is and how to implement it in an express server. In this tutorial, we will try to establish how GrpahQL uses resolvers to fill data in a request and how to write the resolvers.
Setting up the Schema
Building a modular schema is fundamental to building a good API. The first step in building a good schema is knowing what the data you want to get out looks like. Here we will want to be able to make three types of queries, the first being pulling information about a book. The second and third are filtering books by authors and getting all of the books we have in the database. We will be using the following JSON object as our "database".
1// Some dummy data
2const books = [
3 {
4 id: 1,
5 title: 'The Great Gatsby',
6 author: 'F. Scott Fitzgerald',
7 },
8 {
9 id: 2,
10 title: 'To Kill a Mockingbird',
11 author: 'Harper Lee',
12 },
13 {
14 id: 3,
15 title: '1984',
16 author: 'George Orwell',
17 },
18];
Before building the Query
object let's define what a book looks like so we are able to return the books as a result. Our book object Book
will match the shape of the array items in the database, having an id
, title
, and author
like below.
1type Book {
2 id: Int
3 title: String
4 author: String
5 }
For the Query
object we will define which routes we want to make available and what data the resolvers will need to function. The book
route is the route that can get a book by its id
. The exclamation point after the Int
for the id
argument tells GraphQL that a parameter is required. The book
route then returns a Book
object which is the same object defined above. The next route is the books
route, which takes the author of the book and filters out any non-matching books. As you can see this parameter is required also. The last route we will have is the all
route which can return our entire database to the client. The brackets around Book
tells GraphQL that the response should be an array of Book
objects.
1type Query {
2 book(id: Int!): Book
3 books(author: String!): [Book]
4 all: [Book]
5 }
Where to put this at?
Up to this point, the code examples have not been "full" code examples. When it comes to building the schema for your API you need to put these objects inside of the buildSchema
function.
1// Construct a schema using GraphQL schema language
2const schema = buildSchema(`
3type Query {
4 book(id: Int!): Book
5 books(author: String!): [Book]
6 all: [Book]
7}
8
9type Book {
10 id: Int
11 title: String
12 author: String
13}
14`);
Building the Resolvers
Resolvers act as the keys in the query, to better understand this here is a GraphQL query.
1{
2 book(id: 3) {
3 title
4 }
5}
This query is asking to get data from the book
function and passes id
as a parameter with a value of one to it. Then it takes the response of the book
function and extracts the title
parameter from it then returns the data.
To build a resolver start with an new object, typically this is called root
, and add the functions you laid out in the previous steps like below.
1// Define the resolvers
2const root = {
3 book: ({ id }) => books.find((book) => book.id === id),
4 books: ({ author }) => books.filter((book) => book.author === author),
5 all: () => books
6};
The root
object contains the book
function that takes the book id as a parameter and then finds the correct book entree to send back. To extract data from the returned object in a resolver you don't have to add a thing. GraphQL automatically handles the extraction of data. For example, now that we have resolvers defined, we can pass the query above and it would return then following:
1{
2 "data": {
3 "book": {
4 "title": "1984"
5 }
6 }
7}
Full Code Example
1import express from "express";
2import { graphqlHTTP } from 'express-graphql';
3import { buildSchema } from 'graphql';
4
5// Construct a schema using GraphQL schema language
6const schema = buildSchema(`
7 type Query {
8 book(id: Int!): Book
9 books(author: String!): [Book]
10 all: [Book]
11 }
12
13 type Book {
14 id: Int
15 title: String
16 author: String
17 }
18`);
19
20// Some dummy data
21const books = [
22 {
23 id: 1,
24 title: 'The Great Gatsby',
25 author: 'F. Scott Fitzgerald',
26 },
27 {
28 id: 2,
29 title: 'To Kill a Mockingbird',
30 author: 'Harper Lee',
31 },
32 {
33 id: 3,
34 title: '1984',
35 author: 'George Orwell',
36 },
37];
38
39// Define the resolvers
40const root = {
41 book: ({ id }) => books.find((book) => book.id === id),
42 books: ({ author }) => books.filter((book) => book.author === author),
43 all: () => books
44};
45
46// Create an express server and a GraphQL endpoint
47const app = express();
48app.use(
49 '/graphql',
50 graphqlHTTP({
51 schema: schema,
52 rootValue: root,
53 graphiql: true,
54 })
55);
56
57// Start the server
58app.listen(4000, () => console.log('Express GraphQL Server Now Running On localhost:4000/graphql'));