Express Interview Questions Common Express.js questions in backend and Node.js interviews. These range from framework fundamentals to architecture decisions and…
Express Interview Questions
Common Express.js questions in backend and Node.js interviews. These range from framework fundamentals to architecture decisions and security - topics that come up in both junior and senior developer interviews.
1. What is Express.js and why would you use it over plain Node.js?
Express is a minimal, unopinionated web framework built on top of Node.js's http module. While you can build an HTTP server in Node.js directly, Express adds conveniences like routing (app.get, app.post etc.), middleware composition, request/response augmentation (req.params, res.json etc.), and a large ecosystem of compatible middleware packages. It handles the repetitive boilerplate of HTTP so you can focus on application logic. "Unopinionated" means Express does not force any particular project structure or tools - you choose what to combine it with.
2. What is middleware in Express and how does it work?
Middleware is a function with the signature (req, res, next). Express passes each request through a stack of middleware functions in the order they are registered with app.use() or route handlers. Each middleware can read/modify req and res, end the response cycle (e.g., res.json()), or call next() to pass control to the next middleware. If a middleware does not call next() and does not send a response, the request hangs. Calling next(err) skips to the nearest error-handling middleware (which has 4 parameters: err, req, res, next).
3. What is the difference between app.use() and app.get()?
app.use() registers middleware that matches all HTTP methods (GET, POST, PUT, DELETE, etc.) and performs prefix matching on the path (app.use('/api') matches /api, /api/users, /api/posts, etc.). app.get() registers a handler for GET requests only and performs exact path matching (plus route params). app.use() is typically used for middleware and mounting routers; app.get() and siblings are used for specific route handlers.
4. How do you handle errors in Express?
Express has a dedicated error-handling mechanism: a middleware with four parameters (err, req, res, next). To trigger it, call next(err) with an error object from any route or middleware. You should have one global error handler registered after all routes. For async route handlers, you must catch errors and pass them to next() - either with try/catch or by wrapping handlers in an asyncHandler utility. Common pattern: create a custom AppError class with statusCode and code properties to distinguish operational errors (expected, safe to expose) from programming errors (unexpected, should not reveal details).
5. What is express.Router() and why use it?
express.Router() creates a mini Express application - a modular, mountable route handler. Instead of defining all routes on the main app object, you define related routes on a Router instance and mount it at a path prefix: app.use('/api/users', usersRouter). This keeps code organized, enables route-level middleware that only applies to that router, and makes it easy to split routes into separate files as the API grows. Routers can also be nested.
6. How would you implement authentication in Express?
The most common approach for REST APIs is JWT (JSON Web Token) authentication. On login, the server verifies credentials, signs a JWT with a secret key containing user claims (id, role), and returns it to the client. The client stores the token and includes it in the Authorization: Bearer <token> header on subsequent requests. An authenticate middleware verifies the token on every protected route: it extracts the token from the header, verifies it with jwt.verify(), and attaches the decoded payload to req.user. Passwords must be hashed with bcrypt before storing. Sessions with express-session and a session store (Redis) are the alternative for traditional web apps.
7. What security measures should every Express API have?
Essential security layers: (1) helmet - sets security HTTP headers (prevents XSS, clickjacking, MIME sniffing). (2) CORS configuration - restrict which origins can call your API. (3) Rate limiting with express-rate-limit - prevent brute force and DDoS. (4) Input validation - validate and sanitize all incoming data (Zod, express-validator). (5) Request size limits - express.json({ limit: '10kb' }) prevents payload attacks. (6) Parameterized queries - prevent SQL injection. (7) Hash passwords with bcrypt. (8) HTTPS in production. (9) Keep dependencies updated and audit with npm audit.
8. How do you structure a large Express application?
Separate concerns into layers: (1) Routes - define endpoints and attach middleware. (2) Controllers - handle HTTP req/res, call services, return responses. (3) Services - business logic, orchestration, should not know about HTTP. (4) Repositories/Models - data access layer. (5) Middleware - auth, validation, logging, error handling. (6) Config - environment variables, database connections. Keep index.ts just for starting the server; create app.ts to configure the Express instance so it can be imported in tests without starting a server. Use dependency injection or a service locator for testability.
9. How do you handle async/await in Express route handlers?
Express does not handle promise rejections automatically in route handlers. If an async route throws or rejects, you must catch the error and pass it to next(err). Two approaches: (1) Explicit try/catch in every handler - verbose but clear. (2) Wrap handlers with an asyncHandler utility: const asyncHandler = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next). Then use it: app.get('/users', asyncHandler(async (req, res) => { ... })). Express 5 (currently in beta) handles async errors automatically without the wrapper.
10. What is the order of middleware and why does it matter?
Express middleware executes in the exact order it is registered. This has important consequences: (1) body-parser/express.json() must come before routes that read req.body. (2) Authentication middleware must come before route handlers that require auth. (3) The 404 handler must come after all routes (otherwise valid routes would be caught by it). (4) The global error handler must come last. A good order is: security middleware (helmet, cors) -> request parsing (json, urlencoded) -> logging -> rate limiting -> routes -> 404 handler -> error handler.