Express GraphQL API — Complete Guide to Building GraphQL APIs with Express
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
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