Skip to content

GraphQL Express — Integrating GraphQL with Express.js

DodaTech Updated 2026-06-28 6 min read

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
â„šī¸ Info

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

  1. What is the advantage of apollo-server-express over apollo-server?
  2. How do you apply Express middleware to the GraphQL endpoint only?
  3. How do you handle file uploads in GraphQL with Express?
  4. What order should middleware be applied?
  5. How do you access Express session data in GraphQL resolvers?

Answers:

  1. apollo-server-express lets you use Express middleware (cors, auth, rate-limiting) and serve REST endpoints alongside GraphQL in the same server.
  2. Use app.use('/graphql', myMiddleware) before server.applyMiddleware(). The middleware is applied only to requests matching the GraphQL path.
  3. Add graphqlUploadExpress middleware before Apollo Server, define scalar Upload in schema, and handle the createReadStream in the resolver.
  4. Security middleware first (helmet, cors), then parsing (json, graphql-upload), then auth/rate-limiting, then Apollo Server.
  5. 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

Can I use other Node.js frameworks with GraphQL?

Yes — Apollo Server supports Express, Koa, Fastify, Lambda (serverless), and Cloudflare Workers. Each has its own package: apollo-server-express, apollo-server-koa, etc.

How do I handle WebSocket subscriptions with Express?

Use apollo-server-express with subscriptions-transport-ws. Attach the subscription server to the HTTP server using SubscriptionServer.create().

Is apollo-server-express slower than standalone?

Negligible difference — Express adds minimal overhead. The benefit of middleware access far outweighs the tiny performance cost.

Can I run multiple Apollo Servers on different paths?

Yes — create multiple ApolloServer instances and mount them on different paths: /graphql/public, /graphql/admin, each with different configuration.

How do I handle health checks?

App Express health check before Apollo Server: app.get('/health', (req, res) => res.json({ status: 'ok' })). Don't rely on GraphQL for health checks.

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
âŦ… Apollo Server
➡ Code Generation

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro