Real-world examples of GraphQL with Node.js: 3 practical builds you can copy
Let’s start with the most common example of using GraphQL with Node.js in the wild: a public product catalog API for an e‑commerce app.
You have:
- A PostgreSQL database with products, categories, and reviews
- A Node.js server (Express or Fastify)
- A GraphQL layer that powers your web app and mobile app
This is one of the best examples of GraphQL with Node.js: 3 practical examples because it shows why GraphQL exists in the first place: clients want to fetch exactly the fields they need, in a single round-trip.
Schema design for the product catalog
Here’s a stripped‑down schema using graphql SDL syntax:
## schema.graphql
type Product {
id: ID!
name: String!
description: String
price: Float!
currency: String!
inStock: Boolean!
category: Category!
averageRating: Float
reviews(limit: Int = 5, after: String): ReviewConnection!
}
type Category {
id: ID!
name: String!
slug: String!
products(limit: Int = 20, after: String): ProductConnection!
}
type Review {
id: ID!
authorName: String!
rating: Int!
comment: String
createdAt: String!
}
type ProductConnection {
edges: [ProductEdge!]!
pageInfo: PageInfo!
}
type ProductEdge {
cursor: String!
node: Product!
}
type ReviewConnection {
edges: [ReviewEdge!]!
pageInfo: PageInfo!
}
type ReviewEdge {
cursor: String!
node: Review!
}
type PageInfo {
hasNextPage: Boolean!
endCursor: String
}
type Query {
product(id: ID!): Product
products(search: String, limit: Int = 20, after: String): ProductConnection!
category(slug: String!): Category
}
``
Patterns in this first **example of GraphQL with Node.js** that are worth copying:
- Cursor-based pagination (`Connection`, `Edge`, `PageInfo`) instead of page/limit
- Arguments on fields (`reviews(limit, after)`) so clients control how much data they fetch
- Computed fields (`averageRating`) resolved from related data
### Node.js server and resolvers
Using Apollo Server with Express:
```bash
npm install @apollo/server express body-parser cors graphql pg dataloader
// server.js
import express from 'express';
import { ApolloServer } from '@apollo/server';
import { expressMiddleware } from '@apollo/server/express4';
import bodyParser from 'body-parser';
import cors from 'cors';
import { Pool } from 'pg';
import DataLoader from 'dataloader';
import fs from 'fs';
const typeDefs = fs.readFileSync('./schema.graphql', 'utf8');
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
// Example of batching with DataLoader
const createProductLoader = () =>
new DataLoader(async (ids) => {
const { rows } = await pool.query(
'SELECT * FROM products WHERE id = ANY($1)',
[ids]
);
const rowMap = new Map(rows.map((r) => [String(r.id), r]));
return ids.map((id) => rowMap.get(String(id)) || null);
});
const resolvers = {
Query: {
product: async (_, { id }, { loaders }) => loaders.product.load(id),
products: async (_, { search, limit, after }) => {
const params = [];
let query = 'SELECT * FROM products';
if (search) {
params.push(`%${search}%`);
query += ` WHERE name ILIKE $${params.length}`;
}
if (after) {
params.push(after);
query += params.length === 1 ? ' WHERE' : ' AND';
query += ` id > $${params.length}`;
}
params.push(limit + 1);
query += ` ORDER BY id ASC LIMIT $${params.length}`;
const { rows } = await pool.query(query, params);
const hasNextPage = rows.length > limit;
const items = hasNextPage ? rows.slice(0, -1) : rows;
return {
edges: items.map((p) => ({ cursor: String(p.id), node: p })),
pageInfo: {
hasNextPage,
endCursor: items.length ? String(items[items.length - 1].id) : null,
},
};
},
category: async (_, { slug }) => {
const { rows } = await pool.query(
'SELECT * FROM categories WHERE slug = $1 LIMIT 1',
[slug]
);
return rows[0] || null;
},
},
Product: {
category: async (product) => {
const { rows } = await pool.query(
'SELECT * FROM categories WHERE id = $1',
[product.category_id]
);
return rows[0];
},
averageRating: async (product) => {
const { rows } = await pool.query(
'SELECT AVG(rating)::float AS avg FROM reviews WHERE product_id = $1',
[product.id]
);
return rows[0].avg;
},
reviews: async (product, { limit, after }) => {
const params = [product.id];
let query = 'SELECT * FROM reviews WHERE product_id = $1';
if (after) {
params.push(after);
query += ` AND id > $${params.length}`;
}
params.push(limit + 1);
query += ` ORDER BY id ASC LIMIT $${params.length}`;
const { rows } = await pool.query(query, params);
const hasNextPage = rows.length > limit;
const items = hasNextPage ? rows.slice(0, -1) : rows;
return {
edges: items.map((r) => ({ cursor: String(r.id), node: r })),
pageInfo: {
hasNextPage,
endCursor: items.length ? String(items[items.length - 1].id) : null,
},
};
},
},
};
const app = express();
const server = new ApolloServer({ typeDefs, resolvers });
await server.start();
app.use(
'/graphql',
cors(),
bodyParser.json(),
expressMiddleware(server, {
context: async () => ({
loaders: {
product: createProductLoader(),
},
}),
})
);
app.listen(4000, () => {
console.log('GraphQL API running on http://localhost:4000/graphql');
});
This first entry in our examples of GraphQL with Node.js: 3 practical examples shows how you can:
- Expose a flexible read API for web and mobile
- Avoid over‑fetching/under‑fetching
- Use Node.js DataLoader to keep database queries under control
In production, you’d add input validation (e.g., with zod or yup), structured logging, and rate limiting at the HTTP layer.
Example 2: Backend‑for‑Frontend (BFF) for a React Native app
The second entry in these examples of GraphQL with Node.js: 3 practical examples is the BFF pattern. This is where GraphQL really shines in 2024–2025, especially for teams juggling web, iOS, and Android.
Scenario:
- Your React Native app talks to a Node.js GraphQL server
- That server aggregates data from several internal REST APIs: users, orders, and notifications
- You want a single query from the app to hydrate the entire home screen
Instead of the mobile app orchestrating multiple REST calls, the BFF takes on that complexity.
BFF schema tailored to the mobile UI
The schema is organized around screens, not databases:
type HomeScreen {
user: User!
recentOrders: [Order!]!
unreadNotificationsCount: Int!
}
type User {
id: ID!
name: String!
avatarUrl: String
}
type Order {
id: ID!
total: Float!
placedAt: String!
status: String!
}
type Query {
homeScreen: HomeScreen!
}
This is a realistic example of GraphQL with Node.js where the schema is shaped by UX needs, not by database tables. The mobile client can simply run:
query HomeScreen {
homeScreen {
user { id name avatarUrl }
recentOrders { id total placedAt status }
unreadNotificationsCount
}
}
Node.js BFF resolvers that orchestrate REST APIs
The BFF’s resolvers call internal services. With modern Node.js (v20+), you can lean on native fetch:
// bff-server.js
import { ApolloServer } from '@apollo/server';
import { expressMiddleware } from '@apollo/server/express4';
import express from 'express';
import bodyParser from 'body-parser';
import cors from 'cors';
const typeDefs = `#graphql
type HomeScreen {
user: User!
recentOrders: [Order!]!
unreadNotificationsCount: Int!
}
type User {
id: ID!
name: String!
avatarUrl: String
}
type Order {
id: ID!
total: Float!
placedAt: String!
status: String!
}
type Query {
homeScreen: HomeScreen!
}
`;
const resolvers = {
Query: {
homeScreen: async (_, __, { userId, fetchFn }) => {
const [userRes, ordersRes, notificationsRes] = await Promise.all([
fetchFn(`https://internal.api.local/users/${userId}`),
fetchFn(`https://internal.api.local/orders?userId=${userId}&limit=5`),
fetchFn(
`https://internal.api.local/notifications/unread-count?userId=${userId}`
),
]);
if (!userRes.ok) throw new Error('User service failed');
const user = await userRes.json();
const orders = ordersRes.ok ? await ordersRes.json() : [];
const { count } = notificationsRes.ok
? await notificationsRes.json()
: { count: 0 };
return {
user,
recentOrders: orders,
unreadNotificationsCount: count,
};
},
},
};
const app = express();
const server = new ApolloServer({ typeDefs, resolvers });
await server.start();
app.use(
'/graphql',
cors(),
bodyParser.json(),
expressMiddleware(server, {
context: async ({ req }) => {
const userId = req.headers['x-user-id'];
if (!userId) throw new Error('Unauthenticated');
return {
userId,
fetchFn: (url, options) =>
fetch(url, {
...options,
headers: {
...(options?.headers || {}),
'x-internal-auth': process.env.INTERNAL_API_TOKEN,
},
}),
};
},
})
);
app.listen(4001, () => {
console.log('BFF GraphQL API on http://localhost:4001/graphql');
});
Patterns to notice in this second entry in our examples of GraphQL with Node.js: 3 practical examples:
- Node.js GraphQL server acting as a BFF, not as a direct DB layer
- Parallel REST calls with
Promise.allto keep latency low - Context function injecting
userIdand a preconfiguredfetchFn
This is one of the best examples of where GraphQL pays off: the mobile team gets a stable contract while backend teams can keep evolving their internal services.
Example 3: Federated gateway for microservices (advanced example of GraphQL with Node.js)
The third entry in these examples of GraphQL with Node.js: 3 practical examples is more advanced but reflects where a lot of larger organizations are heading in 2024–2025: GraphQL federation.
Scenario:
- You have separate Node.js services: accounts, inventory, payments
- Each service exposes its own GraphQL schema
- A Node.js GraphQL gateway composes them into a single graph
This is one of the more realistic examples include patterns used at companies like Netflix, Shopify, and others that have publicly written about GraphQL adoption.
Service schemas (subgraphs)
A simplified accounts service:
## accounts.graphql
type User @key(fields: "id") {
id: ID!
email: String!
name: String
}
type Query {
me: User
}
An orders service that extends User:
## orders.graphql
type Order {
id: ID!
total: Float!
createdAt: String!
}
type User @key(fields: "id") {
id: ID! @external
orders: [Order!]!
}
Each subgraph is backed by its own Node.js server. Then you add a gateway.
Node.js Apollo Gateway
Using Apollo Gateway (v2+):
npm install @apollo/server @apollo/gateway express body-parser cors graphql
// gateway.js
import { ApolloServer } from '@apollo/server';
import { ApolloGateway } from '@apollo/gateway';
import { expressMiddleware } from '@apollo/server/express4';
import express from 'express';
import bodyParser from 'body-parser';
import cors from 'cors';
const gateway = new ApolloGateway({
supergraphSdl: new IntrospectionSupergraphManager({
subgraphs: [
{ name: 'accounts', url: 'http://localhost:5001/graphql' },
{ name: 'orders', url: 'http://localhost:5002/graphql' },
],
}),
});
const server = new ApolloServer({
gateway,
});
await server.start();
const app = express();
app.use(
'/graphql',
cors(),
bodyParser.json(),
expressMiddleware(server, {
context: async ({ req }) => ({
authHeader: req.headers.authorization || '',
}),
})
);
app.listen(4002, () => {
console.log('Federated GraphQL gateway on http://localhost:4002/graphql');
});
This advanced example of GraphQL with Node.js shows how you can:
- Let each team own its service and schema
- Compose them into a single graph for clients
- Evolve services independently without breaking consumers
In production, teams typically add schema registries, CI checks for breaking changes, and observability. The GraphQL Foundation maintains up‑to‑date guidance on schema design and tooling, and the Node.js project itself continues to optimize for async workloads that fit this pattern.
Other real examples of GraphQL with Node.js in 2024–2025
So far we’ve walked through examples of GraphQL with Node.js: 3 practical examples in depth. But in the real world, teams combine these patterns in different ways. A few more concrete scenarios you’re likely to see:
- Analytics dashboard API: A Node.js GraphQL server exposes metrics aggregated from time‑series databases and third‑party APIs. It uses persisted queries and caching to keep expensive analytics queries under control.
- Feature‑flag management API: Internal tools teams expose flags and targeting rules over GraphQL so frontend teams can query exactly the flags they need. Node.js resolvers read from Redis and a configuration store.
- Content management (headless CMS) layer: Editorial teams manage content in a CMS, but apps consume it via a Node.js GraphQL gateway that normalizes multiple content sources into a single schema.
- IoT control plane: A Node.js GraphQL API acts as a control layer for devices, while actual telemetry still flows over MQTT or Kafka. GraphQL is used for querying device state and sending control commands.
These aren’t hypothetical. They mirror patterns discussed in conference talks and case studies from large engineering orgs. For ongoing trends, the Linux Foundation’s reports often touch on API and microservices adoption, and they line up well with the shift toward schema‑driven APIs like these.
FAQ: common questions about examples of GraphQL with Node.js
Are these examples of GraphQL with Node.js production‑ready?
They’re intentionally simplified. The core ideas are production‑worthy: schema‑first design, pagination, BFF orchestration, and federation. To harden them, you’d add:
- Authentication and authorization (JWT, OAuth 2.0, or your SSO provider)
- Input validation and error handling
- Observability (structured logs, traces, metrics)
- Caching and rate limiting
The patterns, not the exact code, are what you should copy into a production stack.
What is the best example of using GraphQL with Node.js for a small team?
If you’re a small team with one web app and one mobile app, the BFF pattern in Example 2 is usually the best starting point. It lets you:
- Hide internal REST and database complexity
- Shape a schema around screens and user flows
- Iterate quickly without coordinating across many backend teams
You can always evolve toward federation later if you outgrow a single service.
Do these examples include subscriptions or real‑time features?
Not in the code above, but GraphQL with Node.js supports subscriptions using WebSockets or Server‑Sent Events. A practical example would be:
- Real‑time order status updates in the e‑commerce API
- Live notification counts in the BFF layer
Libraries like graphql-ws or subscriptions-transport-ws can plug into Apollo Server or other Node.js GraphQL servers to add this without rewriting your schema.
Where can I find more real examples of GraphQL with Node.js code?
Several open‑source projects and organizations publish real examples:
- The official GraphQL.org site links to JavaScript and Node.js reference implementations
- Many university courses and labs hosted on
.edudomains use Node.js GraphQL in their backend curriculum - Large open‑source projects on GitHub (search for
"apollo-server" language:JavaScript) often have production‑like setups you can study
Use these as references, but keep your own schema focused on your product’s actual use cases.
If you remember nothing else from this guide, remember this: the best examples of GraphQL with Node.js: 3 practical examples are the ones that mirror your real data flows. Start with a small, concrete slice—like the product catalog or a single mobile screen—ship it, measure it, and expand from there.
Related Topics
Best examples of using fragments in GraphQL API (with real patterns)
Practical examples of using directives in GraphQL (with real patterns)
Real-world examples of GraphQL with Node.js: 3 practical builds you can copy
Real-world examples of optimizing GraphQL queries
Real-world examples of GraphQL with TypeScript: practical examples for 2025
Examples of Schema Definition in GraphQL: 3 Practical Examples for Real APIs
Explore More GraphQL API Examples
Discover more examples and insights in this category.
View All GraphQL API Examples