Build a GraphQL API with Node.js and Apollo Server
A GraphQL server needs two things: a schema describing types and operations, and resolvers that fetch the data for each field.
Setup
npm install @apollo/server graphql
Schema and resolvers
// server.js
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { GraphQLError } from 'graphql';
const typeDefs = `#graphql
type Book {
id: ID!
title: String!
author: Author!
}
type Author {
id: ID!
name: String!
books: [Book!]!
}
type Query {
books(limit: Int = 20): [Book!]!
book(id: ID!): Book
}
type Mutation {
addBook(title: String!, authorId: ID!): Book!
}
`;
const resolvers = {
Query: {
books: (_, { limit }, { db }) => db.books.list({ limit }),
book: (_, { id }, { db }) => db.books.find(id),
},
Mutation: {
addBook: async (_, { title, authorId }, { db, user }) => {
if (!user) throw new GraphQLError('Sign in to add books', { extensions: { code: 'UNAUTHENTICATED' } });
return db.books.create({ title, authorId });
},
},
Book: {
author: (book, _, { db }) => db.authors.find(book.authorId),
},
Author: {
books: (author, _, { db }) => db.books.byAuthor(author.id),
},
};
const server = new ApolloServer({ typeDefs, resolvers });
const { url } = await startStandaloneServer(server, {
listen: { port: 4000 },
context: async ({ req }) => ({ db, user: await userFromToken(req.headers.authorization) }),
});
console.log(`GraphQL ready at ${url}`);
How resolution works
Query.booksreturns book rows.- For each book, if the query asked for
author,Book.authorruns with the book as its parent. - Fields without a resolver return the property of the same name from the parent object.
Next steps for production
- Batch nested lookups with DataLoader to avoid N+1 queries.
- Limit query depth and complexity.
- Generate TypeScript types from the schema with GraphQL Code Generator.
- Put authorisation checks in resolvers or a shared business layer, not in the schema alone.
Was this article helpful?
Comments
No comments yet. Have a question or a tip? Be the first to share it.