Toolmingo
Guides21 min read

50 Express.js Interview Questions (With Answers)

Top Express.js interview questions with detailed answers and code examples — covering middleware, routing, REST APIs, authentication, error handling, and performance.

Express.js is the most widely deployed Node.js web framework. Interviews test your understanding of middleware architecture, routing, async error handling, REST design, and security. This guide covers the 50 most common questions — with concise answers and code examples.

Quick reference

Topic Most asked questions
Core concepts What is Express, middleware chain, req/res cycle
Routing Router, path params, query strings, nested routes
Middleware Built-in, third-party, error-handling, order matters
REST APIs CRUD patterns, status codes, response formats
Authentication JWT, session-based, Passport.js
Error handling Sync vs async errors, global handler
Performance Compression, caching, clustering
Security Helmet, CORS, rate limiting, input validation

Core Concepts

1. What is Express.js and why use it?

Express is a minimal, unopinionated web framework for Node.js that adds routing, middleware support, and HTTP utilities on top of Node's built-in http module.

Feature Node http Express
Routing Manual if (url === '/users') app.get('/users', handler)
Middleware Roll your own app.use(fn) chain
Request parsing Manual Buffer assembly express.json() built-in
Response helpers res.end(JSON.stringify(data)) res.json(data)
Error handling Try/catch per handler Centralised 4-arg handler
const express = require('express');
const app = express();

app.get('/', (req, res) => res.send('Hello, Express!'));

app.listen(3000, () => console.log('Listening on port 3000'));

2. Explain the Express request-response cycle.

  1. Incoming HTTP request hits Node's http.Server
  2. Express matches the path/method to a route
  3. The middleware stack executes left-to-right / top-to-bottom
  4. Each middleware calls next() to pass control, or sends a response to end the cycle
  5. A route handler sends the final response
app.use((req, res, next) => {
  console.log(`${req.method} ${req.url}`); // middleware
  next();
});

app.get('/users', (req, res) => {
  res.json([{ id: 1, name: 'Alice' }]); // response — cycle ends
});

3. What is middleware in Express?

A middleware is a function with the signature (req, res, next). It can:

  • Execute any code
  • Modify req/res
  • End the request-response cycle
  • Call next() to pass to the next middleware
// Logger middleware
const logger = (req, res, next) => {
  const start = Date.now();
  res.on('finish', () => {
    console.log(`${req.method} ${req.url} ${res.statusCode} ${Date.now() - start}ms`);
  });
  next();
};

app.use(logger);

4. What are the types of middleware in Express?

Type How registered Example
Application-level app.use() / app.METHOD() logging, auth
Router-level router.use() scoped to a prefix
Error-handling app.use((err, req, res, next) => {}) global error handler
Built-in express.json(), express.static() body parsing, static files
Third-party npm install cors helmet CORS, security headers

5. What does next() do? What is next(err)?

next() (no argument) passes control to the next matching middleware or route handler.

next(err) (with an argument) skips all remaining regular middleware and jumps directly to the error-handling middleware (4-argument form).

app.get('/user/:id', (req, res, next) => {
  if (isNaN(req.params.id)) {
    return next(new Error('Invalid ID')); // jump to error handler
  }
  res.json({ id: req.params.id });
});

// Error-handling middleware (must have 4 params)
app.use((err, req, res, next) => {
  res.status(400).json({ error: err.message });
});

6. What is the difference between app.use() and app.get()?

app.use() app.get()
HTTP method All methods GET only
Path matching Prefix match (/api matches /api/users) Exact match
Use case Middleware, sub-routers Route handlers
app.use('/api', apiRouter);       // mounts router at /api/*
app.get('/api/users', handler);   // exact GET /api/users only

Routing

7. How do you define route parameters?

Use :paramName in the path. Access via req.params.

app.get('/users/:id', (req, res) => {
  res.json({ userId: req.params.id });
});

// Optional param
app.get('/posts/:year/:month?', (req, res) => {
  res.json(req.params); // { year: '2025', month: '07' }
});

8. What are query strings and how do you access them?

Query strings appear after ? in the URL (/search?q=express&page=2). Access via req.query.

app.get('/search', (req, res) => {
  const { q = '', page = 1, limit = 10 } = req.query;
  res.json({ query: q, page: Number(page), limit: Number(limit) });
});

9. What is express.Router()?

Router is a mini Express application — it has its own middleware stack and routes but no listen(). Use it to modularise routes.

// routes/users.js
const router = require('express').Router();

router.get('/', getAllUsers);
router.get('/:id', getUser);
router.post('/', createUser);
router.put('/:id', updateUser);
router.delete('/:id', deleteUser);

module.exports = router;

// app.js
const usersRouter = require('./routes/users');
app.use('/users', usersRouter); // all routes prefixed with /users

10. How do you handle multiple HTTP methods on the same path?

Use app.route() to chain methods:

app.route('/users/:id')
  .get((req, res) => res.json(getUser(req.params.id)))
  .put((req, res) => res.json(updateUser(req.params.id, req.body)))
  .delete((req, res) => { deleteUser(req.params.id); res.status(204).end(); });

11. How do you serve static files in Express?

Use the built-in express.static() middleware:

// Serve files from the 'public' directory
app.use(express.static('public'));

// With virtual path prefix
app.use('/assets', express.static('public'));
// GET /assets/logo.png → ./public/logo.png

Request Parsing

12. How do you parse JSON request bodies?

Use the built-in express.json() middleware (replaces the deprecated body-parser):

app.use(express.json());
app.use(express.urlencoded({ extended: true })); // for form data

app.post('/users', (req, res) => {
  console.log(req.body); // { name: 'Alice', email: 'alice@example.com' }
  res.status(201).json(req.body);
});

13. What is the difference between req.params, req.query, and req.body?

req.params req.query req.body
Source URL path URL after ? Request body
Example URL /users/42 /users?page=2 POST body
Middleware needed None None express.json()
Type String always String/Array Depends on body
// GET /users/42?fields=name,email + POST body: {"role": "admin"}
app.put('/users/:id', (req, res) => {
  const id = req.params.id;        // '42'
  const fields = req.query.fields; // 'name,email'
  const data = req.body;           // { role: 'admin' }
});

14. How do you handle file uploads in Express?

Use the multer middleware:

const multer = require('multer');
const upload = multer({
  dest: 'uploads/',
  limits: { fileSize: 5 * 1024 * 1024 }, // 5 MB
  fileFilter: (req, file, cb) => {
    if (!file.mimetype.startsWith('image/')) {
      return cb(new Error('Only images allowed'));
    }
    cb(null, true);
  }
});

app.post('/avatar', upload.single('photo'), (req, res) => {
  res.json({ filename: req.file.filename });
});

Response Methods

15. What response methods does Express provide?

Method Description
res.send() Send string, Buffer, or object
res.json() Send JSON + set Content-Type: application/json
res.status(code) Set HTTP status code
res.redirect(url) Redirect to URL (302 default)
res.render(view) Render a template
res.sendFile(path) Stream a file
res.download(path) Trigger file download
res.end() End response with no body
res.set(header, value) Set response header
res.cookie(name, val) Set cookie
res.status(201).json({ id: 1, name: 'Alice' });
res.status(404).json({ error: 'Not found' });
res.redirect(301, 'https://example.com/new-url');

16. How do you set response headers in Express?

app.get('/data', (req, res) => {
  res.set('X-Custom-Header', 'value');
  res.set({
    'Cache-Control': 'no-store',
    'Content-Type': 'application/json'
  });
  res.json({ ok: true });
});

Error Handling

17. How do you handle errors in Express?

Define a 4-argument middleware (err, req, res, next) after all routes:

// Sync errors — throw inside handlers
app.get('/sync', (req, res) => {
  throw new Error('Sync error'); // Express 5 catches automatically
});

// Async errors — pass to next(err)
app.get('/async', async (req, res, next) => {
  try {
    const data = await fetchData();
    res.json(data);
  } catch (err) {
    next(err);
  }
});

// Error handler (must be last)
app.use((err, req, res, next) => {
  console.error(err.stack);
  const status = err.status || 500;
  res.status(status).json({
    error: {
      message: err.message,
      ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
    }
  });
});

18. What is the difference between Express 4 and Express 5 error handling?

Express 4 Express 5
Async errors Must wrap in try/catch + next(err) async handlers auto-forward rejections
next(err) still works Yes Yes
app.use(errorHandler) Yes Yes
Release status Stable Stable (2024)

Express 5 makes async error handling much simpler — thrown errors and rejected promises are automatically passed to error middleware.


19. How do you create a custom error class?

class AppError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.statusCode = statusCode;
    this.isOperational = true;
    Error.captureStackTrace(this, this.constructor);
  }
}

// Usage
app.get('/users/:id', async (req, res, next) => {
  const user = await User.findById(req.params.id);
  if (!user) throw new AppError('User not found', 404);
  res.json(user);
});

// Error handler checks isOperational
app.use((err, req, res, next) => {
  if (err.isOperational) {
    return res.status(err.statusCode).json({ error: err.message });
  }
  // Unknown error — don't leak details
  console.error('UNEXPECTED:', err);
  res.status(500).json({ error: 'Internal server error' });
});

20. How do you handle 404 routes in Express?

Add a catch-all middleware after all routes:

// Specific routes above...
app.get('/users', handler);

// 404 handler — no route matched
app.use((req, res, next) => {
  next(new AppError(`Route ${req.method} ${req.url} not found`, 404));
});

// Error handler
app.use((err, req, res, next) => {
  res.status(err.statusCode || 500).json({ error: err.message });
});

Middleware Deep Dive

21. What is the order of middleware execution and why does it matter?

Express executes middleware in the order they are defined. A middleware registered after a route will not run for that route.

// CORRECT: auth runs before routes
app.use(authMiddleware);
app.get('/protected', handler);

// WRONG: auth never runs for /protected
app.get('/protected', handler);
app.use(authMiddleware); // too late

22. What is a parameterised middleware (middleware factory)?

A function that returns a middleware — useful for configuration:

const rateLimit = (windowMs, max) => {
  const store = new Map();
  return (req, res, next) => {
    const key = req.ip;
    const now = Date.now();
    const entry = store.get(key) || { count: 0, resetAt: now + windowMs };
    if (now > entry.resetAt) { entry.count = 0; entry.resetAt = now + windowMs; }
    entry.count++;
    store.set(key, entry);
    if (entry.count > max) return res.status(429).json({ error: 'Too Many Requests' });
    next();
  };
};

app.use('/api', rateLimit(60_000, 100)); // 100 req/min

23. How do you write an async middleware wrapper to avoid try/catch in every handler?

// Express 4 utility — wraps async handlers
const asyncHandler = (fn) => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};

// Usage
app.get('/users', asyncHandler(async (req, res) => {
  const users = await User.find();
  res.json(users);
}));
// No try/catch needed — errors automatically go to next(err)

Express 5 makes this unnecessary as async handlers auto-forward rejections.


24. What built-in middleware does Express 4 provide?

Middleware Purpose
express.json() Parse application/json bodies
express.urlencoded() Parse URL-encoded form bodies
express.static() Serve static files
express.raw() Parse raw binary bodies
express.text() Parse plain text bodies

REST API Design

25. What HTTP status codes should a REST API return?

Scenario Status Code
Successful GET 200 OK
Resource created 201 Created
No content (DELETE) 204 No Content
Bad request 400 Bad Request
Unauthenticated 401 Unauthorized
Forbidden (no permission) 403 Forbidden
Not found 404 Not Found
Conflict (duplicate) 409 Conflict
Validation error 422 Unprocessable Entity
Rate limited 429 Too Many Requests
Server error 500 Internal Server Error

26. How do you build a RESTful CRUD API in Express?

const router = require('express').Router();
const db = require('../db'); // your data layer

router.get('/', async (req, res, next) => {
  try {
    const items = await db.findAll();
    res.json(items);
  } catch (err) { next(err); }
});

router.get('/:id', async (req, res, next) => {
  try {
    const item = await db.findById(req.params.id);
    if (!item) return res.status(404).json({ error: 'Not found' });
    res.json(item);
  } catch (err) { next(err); }
});

router.post('/', async (req, res, next) => {
  try {
    const item = await db.create(req.body);
    res.status(201).json(item);
  } catch (err) { next(err); }
});

router.put('/:id', async (req, res, next) => {
  try {
    const item = await db.update(req.params.id, req.body);
    if (!item) return res.status(404).json({ error: 'Not found' });
    res.json(item);
  } catch (err) { next(err); }
});

router.delete('/:id', async (req, res, next) => {
  try {
    await db.delete(req.params.id);
    res.status(204).end();
  } catch (err) { next(err); }
});

module.exports = router;

27. How do you version your Express API?

Option 1: URL path versioning (most common)

app.use('/api/v1', v1Router);
app.use('/api/v2', v2Router);

Option 2: Header versioning

app.use((req, res, next) => {
  const version = req.headers['api-version'] || '1';
  req.apiVersion = version;
  next();
});

Option 3: Query string

// GET /api/users?version=2

URL versioning is preferred for simplicity and cacheability.


28. How do you implement pagination in Express?

app.get('/users', async (req, res, next) => {
  const page = Math.max(1, parseInt(req.query.page) || 1);
  const limit = Math.min(100, parseInt(req.query.limit) || 20);
  const skip = (page - 1) * limit;

  try {
    const [users, total] = await Promise.all([
      User.find().skip(skip).limit(limit),
      User.countDocuments()
    ]);
    res.json({
      data: users,
      pagination: {
        page, limit, total,
        totalPages: Math.ceil(total / limit),
        hasNext: page < Math.ceil(total / limit),
        hasPrev: page > 1
      }
    });
  } catch (err) { next(err); }
});

Authentication

29. How do you implement JWT authentication in Express?

const jwt = require('jsonwebtoken');
const SECRET = process.env.JWT_SECRET;

// Login endpoint
app.post('/auth/login', async (req, res) => {
  const { email, password } = req.body;
  const user = await User.findOne({ email });
  if (!user || !(await user.comparePassword(password))) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }
  const token = jwt.sign({ sub: user.id, role: user.role }, SECRET, { expiresIn: '15m' });
  const refreshToken = jwt.sign({ sub: user.id }, SECRET, { expiresIn: '7d' });
  res.json({ token, refreshToken });
});

// Auth middleware
const auth = (req, res, next) => {
  const header = req.headers.authorization;
  if (!header?.startsWith('Bearer ')) return res.status(401).json({ error: 'Unauthorized' });
  try {
    req.user = jwt.verify(header.slice(7), SECRET);
    next();
  } catch {
    res.status(401).json({ error: 'Invalid or expired token' });
  }
};

// Protected route
app.get('/profile', auth, (req, res) => {
  res.json({ userId: req.user.sub });
});

30. What is Passport.js and how does it integrate with Express?

Passport is an authentication middleware for Node.js that supports 500+ strategies (local, OAuth, JWT, SAML, etc.).

const passport = require('passport');
const { Strategy: LocalStrategy } = require('passport-local');

passport.use(new LocalStrategy(
  { usernameField: 'email' },
  async (email, password, done) => {
    const user = await User.findOne({ email });
    if (!user) return done(null, false, { message: 'User not found' });
    if (!(await user.comparePassword(password))) {
      return done(null, false, { message: 'Wrong password' });
    }
    done(null, user);
  }
));

app.use(passport.initialize());

app.post('/login',
  passport.authenticate('local', { session: false }),
  (req, res) => res.json({ user: req.user })
);

31. How do you implement session-based authentication?

const session = require('express-session');
const RedisStore = require('connect-redis').default;

app.use(session({
  store: new RedisStore({ client: redisClient }),
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: {
    secure: process.env.NODE_ENV === 'production',
    httpOnly: true,
    maxAge: 24 * 60 * 60 * 1000 // 1 day
  }
}));

app.post('/login', async (req, res) => {
  const user = await authenticate(req.body);
  if (!user) return res.status(401).json({ error: 'Invalid credentials' });
  req.session.userId = user.id;
  res.json({ message: 'Logged in' });
});

const requireAuth = (req, res, next) => {
  if (!req.session.userId) return res.status(401).json({ error: 'Unauthorized' });
  next();
};

Security

32. How do you add security headers to an Express app?

Use helmet:

const helmet = require('helmet');

app.use(helmet()); // Sets 11 security headers including:
// Content-Security-Policy
// X-Frame-Options: SAMEORIGIN
// X-Content-Type-Options: nosniff
// Strict-Transport-Security (HSTS)
// Referrer-Policy

// Custom CSP
app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'", 'cdn.example.com'],
    styleSrc: ["'self'", "'unsafe-inline'"],
  }
}));

33. How do you configure CORS in Express?

const cors = require('cors');

// Allow all origins (not for production)
app.use(cors());

// Configured CORS
const corsOptions = {
  origin: (origin, callback) => {
    const allowedOrigins = ['https://app.example.com', 'https://admin.example.com'];
    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
  maxAge: 86400 // preflight cache 24h
};

app.use(cors(corsOptions));

34. How do you prevent SQL injection and XSS in Express?

SQL Injection — always use parameterised queries:

// WRONG — SQL injection vulnerable
const users = await db.query(`SELECT * FROM users WHERE id = ${req.params.id}`);

// CORRECT — parameterised
const users = await db.query('SELECT * FROM users WHERE id = ?', [req.params.id]);

Input validation — use express-validator or zod:

const { body, validationResult } = require('express-validator');

app.post('/users',
  body('email').isEmail().normalizeEmail(),
  body('name').trim().isLength({ min: 2, max: 50 }),
  body('age').isInt({ min: 0, max: 150 }),
  (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(422).json({ errors: errors.array() });
    }
    // safe to use req.body now
  }
);

XSS — escape output or use a template engine that auto-escapes. Never insert untrusted HTML directly.


35. How do you implement rate limiting in Express?

const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // max 100 requests per window
  message: { error: 'Too many requests, try again later.' },
  standardHeaders: true, // Return X-RateLimit-* headers
  legacyHeaders: false,
});

// Apply to all API routes
app.use('/api', limiter);

// Stricter limit for auth endpoints
const authLimiter = rateLimit({ windowMs: 60_000, max: 5 });
app.use('/auth', authLimiter);

Performance

36. How do you compress responses in Express?

const compression = require('compression');

app.use(compression({
  threshold: 1024,     // only compress responses > 1 KB
  level: 6,            // gzip compression level (1-9)
  filter: (req, res) => {
    if (req.headers['x-no-compression']) return false;
    return compression.filter(req, res);
  }
}));

37. How do you implement caching in Express?

HTTP caching with headers:

app.get('/static-data', (req, res) => {
  res.set('Cache-Control', 'public, max-age=3600'); // cache 1 hour
  res.json(staticData);
});

In-memory caching with node-cache:

const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 300 }); // 5 min TTL

const cacheMiddleware = (req, res, next) => {
  const key = req.originalUrl;
  const cached = cache.get(key);
  if (cached) return res.json(cached);

  const originalJson = res.json.bind(res);
  res.json = (data) => {
    cache.set(key, data);
    originalJson(data);
  };
  next();
};

app.get('/users', cacheMiddleware, getAllUsers);

38. How do you use clustering to utilise multiple CPU cores?

const cluster = require('cluster');
const os = require('os');

if (cluster.isPrimary) {
  const numCPUs = os.cpus().length;
  console.log(`Master ${process.pid} starting ${numCPUs} workers`);
  for (let i = 0; i < numCPUs; i++) cluster.fork();
  cluster.on('exit', (worker) => {
    console.log(`Worker ${worker.pid} died — restarting`);
    cluster.fork();
  });
} else {
  const app = require('./app');
  app.listen(3000, () => console.log(`Worker ${process.pid} listening`));
}

In production, prefer PM2 (pm2 start app.js -i max) which handles clustering, restarts, and logging.


Testing

39. How do you test Express routes?

Use supertest to make HTTP requests to the app without starting a real server:

const request = require('supertest');
const app = require('../app');

describe('GET /users', () => {
  it('returns a list of users', async () => {
    const res = await request(app)
      .get('/users')
      .set('Authorization', `Bearer ${testToken}`);

    expect(res.status).toBe(200);
    expect(Array.isArray(res.body)).toBe(true);
  });

  it('returns 401 without auth', async () => {
    const res = await request(app).get('/users');
    expect(res.status).toBe(401);
  });
});

describe('POST /users', () => {
  it('creates a user with valid data', async () => {
    const res = await request(app)
      .post('/users')
      .send({ name: 'Alice', email: 'alice@example.com' });

    expect(res.status).toBe(201);
    expect(res.body).toMatchObject({ name: 'Alice' });
  });
});

40. How do you test middleware in isolation?

const authMiddleware = require('../middleware/auth');

describe('auth middleware', () => {
  const mockReq = (headers = {}) => ({ headers });
  const mockRes = () => {
    const res = {};
    res.status = jest.fn().mockReturnValue(res);
    res.json = jest.fn().mockReturnValue(res);
    return res;
  };
  const mockNext = jest.fn();

  it('calls next() with valid token', () => {
    const req = mockReq({ authorization: `Bearer ${validToken}` });
    const res = mockRes();
    authMiddleware(req, res, mockNext);
    expect(mockNext).toHaveBeenCalled();
    expect(mockNext).not.toHaveBeenCalledWith(expect.any(Error));
  });

  it('returns 401 without token', () => {
    const req = mockReq({});
    const res = mockRes();
    authMiddleware(req, res, mockNext);
    expect(res.status).toHaveBeenCalledWith(401);
  });
});

Advanced Topics

41. How do you structure a large Express application?

src/
├── app.js              # Express setup, middleware registration
├── server.js           # http.createServer, port binding
├── config/
│   ├── index.js        # Environment config
│   └── database.js     # DB connection
├── routes/
│   ├── index.js        # Mount all routers
│   ├── users.js        # User routes
│   └── products.js     # Product routes
├── controllers/
│   ├── userController.js
│   └── productController.js
├── services/           # Business logic
├── middleware/
│   ├── auth.js
│   ├── validate.js
│   └── errorHandler.js
├── models/             # Database models
└── utils/              # Shared utilities

42. What is the difference between app.js and server.js?

Separating them enables testing without starting a server:

// app.js — Express setup only
const express = require('express');
const app = express();
app.use(express.json());
app.use('/users', require('./routes/users'));
app.use(require('./middleware/errorHandler'));
module.exports = app;

// server.js — starts listening
const app = require('./app');
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server on port ${PORT}`));

Supertest imports app.js directly without calling listen().


43. How do you implement graceful shutdown?

const server = app.listen(PORT);

const shutdown = async (signal) => {
  console.log(`${signal} received — starting graceful shutdown`);
  server.close(async () => {
    await db.disconnect();
    console.log('Server closed, process exiting');
    process.exit(0);
  });
  // Force exit after 30s
  setTimeout(() => process.exit(1), 30_000).unref();
};

process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));

44. How do you use template engines with Express?

// EJS
app.set('view engine', 'ejs');
app.set('views', './views');

app.get('/home', (req, res) => {
  res.render('index', { title: 'My App', user: req.user });
});
// views/index.ejs: <h1><%= title %></h1> <p>Hello, <%= user.name %></p>

// Handlebars
const { engine } = require('express-handlebars');
app.engine('handlebars', engine());
app.set('view engine', 'handlebars');

45. How do you implement WebSockets alongside Express?

const express = require('express');
const { createServer } = require('http');
const { Server } = require('socket.io');

const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer, { cors: { origin: '*' } });

io.on('connection', (socket) => {
  console.log(`Client connected: ${socket.id}`);
  socket.on('message', (data) => {
    io.emit('message', data); // broadcast to all
  });
  socket.on('disconnect', () => console.log(`Client disconnected: ${socket.id}`));
});

// Regular Express routes still work
app.get('/api/status', (req, res) => res.json({ ok: true }));

httpServer.listen(3000);

46. How do you handle environment variables in Express?

// .env
// PORT=3000
// DB_URL=mongodb://localhost:27017/mydb
// JWT_SECRET=supersecret

// Load at entry point (server.js)
require('dotenv').config();

// config/index.js — typed, validated config
const config = {
  port: parseInt(process.env.PORT || '3000'),
  db: {
    url: process.env.DB_URL,
  },
  jwt: {
    secret: process.env.JWT_SECRET,
    expiresIn: '15m'
  },
  isDev: process.env.NODE_ENV !== 'production'
};

// Fail fast if required env vars are missing
const required = ['DB_URL', 'JWT_SECRET'];
for (const key of required) {
  if (!process.env[key]) throw new Error(`Missing required env var: ${key}`);
}

module.exports = config;

47. How do you implement request logging in Express?

Development: Use morgan:

const morgan = require('morgan');
app.use(morgan('dev')); // "GET /users 200 12ms"

Production: Use structured logging with winston or pino:

const pino = require('pino');
const logger = pino({ level: 'info' });

app.use((req, res, next) => {
  const start = Date.now();
  res.on('finish', () => {
    logger.info({
      method: req.method,
      url: req.url,
      status: res.statusCode,
      duration: Date.now() - start,
      ip: req.ip,
    });
  });
  next();
});

48. What is express-validator and how does it work?

const { body, param, query, validationResult } = require('express-validator');

const validateCreateUser = [
  body('name').trim().notEmpty().withMessage('Name is required')
    .isLength({ max: 50 }).withMessage('Name max 50 chars'),
  body('email').isEmail().normalizeEmail(),
  body('age').optional().isInt({ min: 18, max: 100 }),
  body('role').isIn(['user', 'admin']).withMessage('Invalid role'),
];

app.post('/users', validateCreateUser, (req, res) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(422).json({ errors: errors.array() });
  }
  // req.body is validated and sanitised
});

Common Anti-Patterns

Anti-pattern Problem Fix
No error-handling middleware Unhandled errors crash the process Add 4-arg (err, req, res, next) handler last
Sending after response Cannot set headers after they are sent crash Use return res.json(...)
Sync blocking in handlers Blocks the event loop for all requests Use async file I/O, non-blocking DB queries
Storing secrets in code Exposes credentials in git Use process.env + .env + dotenv
No input validation SQL injection, XSS, logic bugs Use express-validator or zod
Missing rate limiting Brute-force attacks on auth Add express-rate-limit to /auth routes
Not using helmet Missing security headers app.use(helmet())
Global app.use(cors()) Allows any origin Configure specific allowed origins

Express vs Other Frameworks

Feature Express Fastify NestJS Koa
Speed (req/s) ~45k ~85k ~40k ~50k
Bundle size Minimal Minimal Large Minimal
TypeScript Manual setup Built-in Built-in Manual
Middleware (req,res,next) Hooks Guards/Interceptors ctx + async
Validation Third-party JSON Schema Decorators Third-party
Learning curve Low Low High Low
Ecosystem Largest Growing Enterprise Minimal
Best for Prototypes, REST APIs High-performance Enterprise, NestJS apps Middleware composition

Frequently Asked Questions

Q: Should I use Express or Fastify for a new project? Fastify is a strong choice if performance is critical — it's ~2× faster than Express and has built-in JSON Schema validation and TypeScript support. Express is better if you need the widest middleware ecosystem or your team already knows it.

Q: What is the difference between Express middleware and a route handler? They're the same thing — both are functions of (req, res, next). The difference is intent: middleware transforms/validates the request or adds cross-cutting behaviour; route handlers produce the final response. Route handlers are middleware that typically don't call next().

Q: Is Express single-threaded? Can it handle concurrent requests? Yes, Node.js (and Express) runs on a single thread, but it handles concurrent requests efficiently through the event loop and non-blocking I/O. CPU-intensive work should be offloaded to Worker Threads or a separate service.

Q: How do I use TypeScript with Express?

npm install express && npm install -D typescript @types/express ts-node
import express, { Request, Response, NextFunction } from 'express';
const app = express();
app.get('/users', (req: Request, res: Response) => { res.json([]); });

Q: What's the best way to handle database connections in Express? Create the connection once at startup (outside the request handler) and reuse it. Use connection pools (e.g., mysql2's createPool, Mongoose's built-in pooling) and close connections on SIGTERM.

Q: Can Express serve a React/Vue SPA? Yes — build the frontend to a dist/ folder, then:

app.use(express.static('dist'));
app.get('*', (req, res) => res.sendFile(path.join(__dirname, 'dist/index.html')));

The wildcard catch ensures client-side routing works.

Keep reading

All Toolmingotools are free & run in your browser

No sign-up, no upload, no watermark. Your files never leave your device.

Browse all tools