GraphQL Express â Integrating GraphQL with Express.js
In this tutorial, you will learn about GraphQL Express. We cover key concepts, practical examples, and best practices to help you master this topic.
GraphQL Express integration combines Apollo Server with Express.js middleware, enabling custom routes, authentication middleware, file uploads, and REST endpoints alongside GraphQL.
What You'll Learn
You will learn how to integrate Apollo Server with Express, add custom middleware, combine REST and GraphQL routes, handle file uploads, and configure CORS and rate limiting.
Why Express Integration Matters
Standalone Apollo Server is convenient but limited when you need custom middleware (CORS, rate limiting, logging) or want to serve REST endpoints alongside GraphQL. DodaTech's Durga Antivirus Pro runs Express as the main HTTP server, mounting GraphQL at /graphql, REST endpoints at /api/v1/, and static files at /docs/ â all in one process.
flowchart TB
A["Express Server"] --> B["/graphql\nApollo Server"]
A --> C["/api/v1/*\nREST endpoints"]
A --> D["/docs\nStatic files"]
A --> E["/health\nHealth check"]
B --> F["Express Middleware\n(cors, auth, rate-limit, logging)"]
style A fill:#dbeafe,stroke:#2563eb
style B fill:#fef3c7,stroke:#d97706
Prerequisites: Express.js and Apollo Server experience. Node.js familiarity.
Basic Setup with apollo-server-express
const express = require('express');
const { ApolloServer, gql } = require('apollo-server-express');
const http = require('http');
async function startServer() {
const app = express();
const typeDefs = gql`
type Device { id: ID! name: String! os: String! }
type Query { devices: [Device!]! }
`;
const resolvers = {
Query: { devices: () => [{ id: '1', name: 'Office-PC', os: 'Windows 11' }] },
};
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => ({
user: req.user, // From Express auth middleware
}),
});
await server.start();
server.applyMiddleware({ app, path: '/graphql' });
const httpServer = http.createServer(app);
httpServer.listen(4000, () => {
console.log(`Server at http://localhost:4000${server.graphqlPath}`);
});
}
startServer();
Custom Express Middleware
const express = require('express');
const rateLimit = require('express-rate-limit');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const { ApolloServer } = require('apollo-server-express');
const app = express();
// Security middleware
app.use(helmet());
app.use(cors({ origin: ['https://dashboard.dodatech.com'] }));
app.use(morgan('combined'));
// Rate limiting
const graphqlLimiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 100, // 100 requests per minute
message: 'Too many requests',
});
// Custom auth middleware
app.use('/graphql', (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (token) {
try {
req.user = jwt.verify(token, process.env.JWT_SECRET);
} catch {
req.user = null;
}
}
next();
});
// Apply GraphQL with rate limiting
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => ({ user: req.user, db }),
});
async function start() {
await server.start();
app.use('/graphql', graphqlLimiter);
server.applyMiddleware({ app, path: '/graphql' });
app.listen(4000);
}
start();
REST Endpoints Alongside GraphQL
const express = require('express');
const { ApolloServer } = require('apollo-server-express');
// REST routes alongside GraphQL
app.get('/api/v1/threats', async (req, res) => {
const threats = await db.threats.findAll();
res.json({ data: threats, count: threats.length });
});
app.post('/api/v1/webhook', express.raw({ type: 'application/json' }), (req, res) => {
// Handle incoming webhooks alongside GraphQL
const signature = req.headers['x-signature'];
if (!verifySignature(req.body, signature)) {
return res.status(401).json({ error: 'Invalid signature' });
}
res.status(200).json({ received: true });
});
// GraphQL endpoint
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => ({ user: req.user, db }),
});
async function start() {
await server.start();
server.applyMiddleware({ app });
app.listen(4000);
console.log('REST at /api/v1/, GraphQL at /graphql');
}
File Uploads with Express and GraphQL
const express = require('express');
const { ApolloServer, gql } = require('apollo-server-express');
const { graphqlUploadExpress } = require('graphql-upload');
const { finished } = require('stream/promises');
const typeDefs = gql`
scalar Upload
type FileResponse {
filename: String!
mimetype: String!
url: String!
size: Int!
}
type Mutation {
uploadScanFile(file: Upload!, deviceId: ID!): FileResponse!
}
`;
const resolvers = {
Mutation: {
uploadScanFile: async (_, { file, deviceId }, context) => {
const { createReadStream, filename, mimetype } = await file;
const stream = createReadStream();
// Stream to cloud storage
const uploadResult = await uploadToS3(stream, filename, deviceId);
return {
filename,
mimetype,
url: uploadResult.url,
size: uploadResult.size,
};
},
},
};
// Express middleware for file uploads
app.use(graphqlUploadExpress({ maxFileSize: 10000000, maxFiles: 10 }));
const server = new ApolloServer({ typeDefs, resolvers });
await server.start();
server.applyMiddleware({ app });
Session Management with Express
const session = require('express-session');
const RedisStore = require('connect-redis').default;
const { ApolloServer } = require('apollo-server-express');
// Express session
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
secure: true,
httpOnly: true,
maxAge: 24 * 60 * 60 * 1000, // 24 hours
},
}));
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => ({
// Access Express session from context
user: req.session?.user,
sessionId: req.sessionID,
db,
}),
plugins: [{
requestDidStart({ context }) {
// Log session activity
if (context.user) {
logger.info(`Query by user ${context.user.id}`);
}
},
}],
});
Common Mistakes
1. Not Awaiting server.start()
applyMiddleware before server.start() throws an error. Always await server.start() first in the async function.
2. Forgetting to Restart HTTP Server
When using apollo-server-express, you must call server.applyMiddleware() before app.listen(). The HTTP server wraps the Express app.
3. Mixing Express Body Parsers
GraphQL handles its own body parsing. If you apply express.json() globally, it may interfere. Apply body parsers conditionally or before GraphQL middleware.
4. Not Handling CORS for GraphQL and REST Differently
REST and GraphQL may need different CORS policies. Apply CORS middleware per-route: app.use('/api', apiCors); app.use('/graphql', graphqlCors).
5. Using File Uploads Without graphql-upload Middleware
Without graphqlUploadExpress, file uploads fail with "Upload is not defined". Add the middleware before Apollo Server.
Practice Questions
- What is the advantage of apollo-server-express over apollo-server?
- How do you apply Express middleware to the GraphQL endpoint only?
- How do you handle file uploads in GraphQL with Express?
- What order should middleware be applied?
- How do you access Express session data in GraphQL resolvers?
Answers:
- apollo-server-express lets you use Express middleware (cors, auth, rate-limiting) and serve REST endpoints alongside GraphQL in the same server.
- Use
app.use('/graphql', myMiddleware)beforeserver.applyMiddleware(). The middleware is applied only to requests matching the GraphQL path. - Add
graphqlUploadExpressmiddleware before Apollo Server, definescalar Uploadin schema, and handle thecreateReadStreamin the resolver. - Security middleware first (helmet, cors), then parsing (json, graphql-upload), then auth/rate-limiting, then Apollo Server.
context: ({ req }) => ({ user: req.session?.user }). The Express request object is available in the context factory.
Challenge: Build an Express server for DodaTech that serves both GraphQL (at /graphql) and REST (at /api/v1/*). Include CORS restricted to dashboard domains, rate limiting at 100 req/min for GraphQL, file upload support for scan files (max 10MB), session management with Redis, and a health check endpoint at /health.
FAQ
Mini Project
Build a complete Express server for DodaTech that integrates GraphQL with authentication middleware, session management, file uploads for scan results, a REST API for Webhooks, health checks, CORS configuration, rate limiting, and request logging with Morgan.
What's Next
| Topic | Description |
|---|---|
| Code Generation | TypeScript types from schema |
| Testing | Testing GraphQL APIs |
| Apollo Server | Production server configuration |
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro