Build a GraphQL API with Node.js and Apollo Server

A
Author
17 Jun, 2026 2 min read 1K views

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

  1. Query.books returns book rows.
  2. For each book, if the query asked for author, Book.author runs with the book as its parent.
  3. 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.

Leave a comment

Comments are reviewed before they appear.