Skip to content

Express GraphQL API — Complete Guide to Building GraphQL APIs with Express

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about Express Graphql API. We cover key concepts, practical examples, and best practices to help you master this topic.

Integrating GraphQL with Express lets you build flexible APIs where clients request exactly the data they need, reducing over-fetching and under-fetching common in REST APIs.

What You'll Learn

By the end of this tutorial, you'll set up Apollo Server with Express, define GraphQL schemas and resolvers, implement queries and mutations, handle authentication, and optimize data loading.

Why GraphQL Matters

GraphQL lets clients specify their data requirements in each request. This eliminates over-fetching (getting too much data) and under-fetching (needing multiple requests), making APIs more efficient.

Real-World Use

A social media app uses GraphQL so the feed endpoint returns only the fields each client needs. The mobile app gets compact data, the web dashboard gets rich data, all from the same endpoint.

GraphQL API Learning Path

flowchart LR
  A[REST API] --> B[GraphQL API]
  B --> C[WebSocket]
  C --> D[Authentication]
  D --> E[Authorization]
  A --> F{You Are Here}
  style F fill:#f90,color:#fff

Apollo Server Setup

npm install @apollo/server express graphql
import { ApolloServer } from "@apollo/server";
import { expressMiddleware } from "@apollo/server/express4";
import express from "express";
const app = express();
const typeDefs = `#graphql
  type Book { id: ID!, title: String!, author: String!, year: Int }
  type Query { books: [Book], book(id: ID!): Book }
  type Mutation { addBook(title: String!, author: String!): Book }
`;
const books = [{ id: "1", title: "GraphQL in Action", author: "Alice", year: 2025 }];
const resolvers = {
  Query: { books: () => books, book: (_, { id }) => books.find(b => b.id === id) },
  Mutation: { addBook: (_, { title, author }) => {
    const book = { id: String(books.length + 1), title, author, year: 2026 };
    books.push(book);
    return book;
  }}
};
const server = new ApolloServer({ typeDefs, resolvers });
await server.start();
app.use("/graphql", express.json(), expressMiddleware(server));

Schema Types

const typeDefs = `#graphql
  enum Role { ADMIN USER GUEST }
  type User { id: ID!, name: String!, email: String!, role: Role!, posts: [Post] }
  type Post { id: ID!, title: String!, content: String!, author: User! }
  type Query { users: [User], posts: [Post] }
  type Mutation { createUser(name: String!, email: String!): User }
`;

Resolvers with Context

const resolvers = {
  Query: {
    users: async (_, __, { db }) => db.users.findAll(),
    posts: async (_, __, { db, user }) => {
      if (!user) throw new Error("Not authenticated");
      return db.posts.findAll();
    }
  },
  User: {
    posts: async (parent, _, { db }) => db.posts.findByUserId(parent.id)
  }
};
const server = new ApolloServer({
  typeDefs,
  resolvers,
  context: async ({ req }) => ({
    user: req.headers.authorization ? { id: "1", role: "ADMIN" } : null,
    db
  })
});

Queries and Mutations

// Query
const GET_BOOKS = `#graphql
  query GetBooks { books { id title author } }
`;
// Mutation
const ADD_BOOK = `#graphql
  mutation AddBook($title: String!, $author: String!) {
    addBook(title: $title, author: $author) { id title }
  }
`;

Common Mistakes

1. N+1 Problem in GraphQL

Multiple resolvers that each make a database query cause N+1 problems. Use DataLoader for batching and Caching.

2. Exposing Internal Details via Schema

The GraphQL schema reveals your data structure. Expose only what clients need. Use aliases to rename fields.

3. Not Implementing Pagination

GraphQL queries without pagination can return massive datasets. Always implement cursor-based or offset-based pagination.

4. Deeply Nested Queries

Clients can request deeply nested data, causing performance issues. Implement query depth and complexity limits.

5. Mutations Without Validation

Validate mutation inputs thoroughly. GraphQL type system catches type errors, but business logic validation is still needed.

Practice Questions

1. What is the difference between REST and GraphQL?

REST has fixed endpoints returning fixed data. GraphQL has one endpoint where clients specify exact data requirements.

2. What are resolvers in GraphQL?

Resolvers are functions that fetch data for each field in the schema. They can call databases, APIs, or compute values.

3. What is the N+1 problem in GraphQL?

When a resolver fetches a list and each item triggers another resolver, causing N+1 database queries. DataLoader batches these.

4. How do you handle authentication in GraphQL?

Use the context function in Apollo Server to decode tokens and attach user info to the context, accessible by all resolvers.

5. Challenge: Create a GraphQL schema and resolvers for a blog with Users, Posts, and Comments.

const typeDefs = `#graphql
  type User { id: ID!, name: String!, posts: [Post] }
  type Post { id: ID!, title: String!, content: String!, author: User!, comments: [Comment] }
  type Comment { id: ID!, text: String!, author: User! }
  type Query { users: [User], posts: [Post] }
  type Mutation { createPost(title: String!, content: String!): Post }
`;

FAQ

When should I use GraphQL over REST?

GraphQL suits complex data requirements, multiple clients (mobile + web), and frequent schema changes. REST is simpler for basic CRUD APIs.

How do I handle file uploads in GraphQL?

Use the graphql-upload package or handle file uploads via REST endpoint while using GraphQL for data operations.

What is introspection in GraphQL?

Introspection lets clients query the schema (available types, fields, queries). Disable in production to hide schema details: introspection: false.

How do I test GraphQL APIs?

Use Apollo Studio Explorer (development) or GraphQL Playground. Write integration tests with supertest.

What is Apollo Server Federation?

Federation lets you split a GraphQL schema across multiple services, composing them into a single unified graph.

Mini Project: Blog GraphQL API

Build a GraphQL API for a blog with Apollo Server and Express.

import { ApolloServer } from "@apollo/server";
import { expressMiddleware } from "@apollo/server/express4";
const typeDefs = `#graphql
  type Post { id: ID!, title: String!, content: String! }
  type Query { posts: [Post], post(id: ID!): Post }
  type Mutation { createPost(title: String!, content: String!): Post, deletePost(id: ID!): Boolean }
`;
let posts = [{ id: "1", title: "Hello", content: "World" }];
const resolvers = {
  Query: { posts: () => posts, post: (_, { id }) => posts.find(p => p.id === id) },
  Mutation: {
    createPost: (_, { title, content }) => {
      const post = { id: String(posts.length + 1), title, content };
      posts.push(post); return post;
    },
    deletePost: (_, { id }) => { posts = posts.filter(p => p.id !== id); return true; }
  }
};
const server = new ApolloServer({ typeDefs, resolvers });
await server.start();
app.use("/graphql", express.json(), expressMiddleware(server));

What's Next

WebSocket SocketIO Realtime Apps Node.js Authentication

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro