Beginner Express.js Interview Questions and Answers

๐Ÿ“‹ Table of Contents โ–พ
  1. Questions & Answers
  2. 📝 Knowledge Check

⚡ Beginner Express.js Interview Questions

This lesson covers the fundamental Express.js concepts every Node.js developer must know. Master routing, middleware, request and response objects, HTTP methods, error handling, and serving static files. These questions mirror what interviewers ask at junior and entry-level backend roles.

Questions & Answers

01 What is Express.js and why is it used with Node.js?

Core Express.js is a minimal, unopinionated web application framework for Node.js. It provides a thin layer of fundamental web application features on top of Node’s built-in http module โ€” routing, middleware, request/response helpers โ€” without obscuring Node’s features.

Why use Express over raw Node.js:

  • Routing โ€” Node’s http module requires manual URL parsing; Express provides a clean, declarative routing API
  • Middleware โ€” plug-in functions that process requests in a pipeline (logging, auth, body parsing)
  • Request/Response helpers โ€” res.json(), res.send(), req.params, req.query โ€” all absent in raw Node
  • Ecosystem โ€” thousands of compatible middleware packages (passport, multer, cors, helmet)
  • Minimal footprint โ€” Express adds very little overhead; your app stays performant and in control
// Raw Node.js HTTP server โ€” verbose, manual
const http = require('http');
http.createServer((req, res) => {
  if (req.url === '/users' && req.method === 'GET') { /* ... */ }
}).listen(3000);

// Express โ€” clean and declarative
const express = require('express');
const app = express();
app.get('/users', (req, res) => res.json(users));
app.listen(3000);

02 How do you create a basic Express server?

Core

# 1. Initialise a project and install Express
npm init -y
npm install express
// server.js โ€” minimal Express application
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

// Built-in middleware โ€” parse JSON request bodies
app.use(express.json());
// Parse URL-encoded form data
app.use(express.urlencoded({ extended: true }));

// Basic route
app.get('/', (req, res) => {
  res.send('Hello, World!');
});

// Start the server
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});

Key objects:

  • express() โ€” creates an application instance
  • app.use() โ€” registers middleware
  • app.get/post/put/delete() โ€” registers route handlers
  • app.listen() โ€” starts the HTTP server on the given port

For development, install nodemon globally (npm i -g nodemon) and run nodemon server.js for automatic restarts on file changes.

03 What is middleware in Express.js? How does it work?

Middleware Middleware functions are the backbone of Express. A middleware function has access to the request object (req), the response object (res), and a next function. It can execute code, modify req/res, end the request-response cycle, or call next() to pass control to the next middleware.

// Middleware signature
function myMiddleware(req, res, next) {
  console.log(`${req.method} ${req.url} โ€” ${new Date().toISOString()}`);
  next(); // pass to next middleware or route handler
}

app.use(myMiddleware); // apply to ALL routes

// Middleware order matters โ€” runs top to bottom
app.use(express.json());       // 1. parse body
app.use(authMiddleware);       // 2. check authentication
app.use('/api', apiRouter);    // 3. route to API handlers

The request lifecycle:

Client Request
  โ†’ express.json()       (parse body)
  โ†’ morgan()             (log request)
  โ†’ authMiddleware()     (verify token)
  โ†’ app.get('/users')    (route handler)
  โ†’ res.json(users)      (send response)
  โ†’ Client Response

If a middleware does NOT call next() and does NOT send a response, the request hangs. Always either call next() or send a response.

04 What are the different types of middleware in Express?

Middleware

  • Application-level middleware โ€” bound to the app instance with app.use() or app.METHOD(). Runs for all routes (or routes matching a path prefix).
  • Router-level middleware โ€” bound to an instance of express.Router(). Same as application-level but scoped to a router.
  • Error-handling middleware โ€” four parameters: (err, req, res, next). Only called when an error is passed to next(err).
  • Built-in middleware โ€” express.json(), express.urlencoded(), express.static() โ€” included with Express 4.16+.
  • Third-party middleware โ€” installed via npm: morgan (logging), cors (cross-origin), helmet (security headers), compression (gzip), multer (file uploads).
// Application-level
app.use((req, res, next) => { console.log('Every request'); next(); });

// Error-handling (4 params โ€” Express detects by arity)
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ error: err.message });
});

// Third-party
const morgan  = require('morgan');
const helmet  = require('helmet');
const cors    = require('cors');
app.use(morgan('dev'));
app.use(helmet());
app.use(cors());

05 What are route parameters, query strings, and request body?

Routing

// ROUTE PARAMETERS โ€” dynamic segments in the URL path prefixed with :
// URL: GET /users/42/posts/7
app.get('/users/:userId/posts/:postId', (req, res) => {
  const { userId, postId } = req.params;
  // userId = "42", postId = "7"
  res.json({ userId, postId });
});

// QUERY STRINGS โ€” key=value pairs after the ? in the URL
// URL: GET /products?category=electronics&sort=price&page=2
app.get('/products', (req, res) => {
  const { category, sort, page = 1 } = req.query;
  // category = "electronics", sort = "price", page = "2"
  res.json({ category, sort, page });
});

// REQUEST BODY โ€” data sent in POST/PUT body (requires express.json() middleware)
// POST /users  body: { "name": "Alice", "email": "alice@example.com" }
app.post('/users', (req, res) => {
  const { name, email } = req.body;
  // name = "Alice", email = "alice@example.com"
  res.status(201).json({ id: 1, name, email });
});

06 What HTTP methods does Express support? What are they used for?

Routing Express provides methods corresponding to all HTTP verbs, following REST conventions:

app.get('/users',          getAll);   // Read all โ€” retrieve a list
app.get('/users/:id',      getOne);   // Read one โ€” retrieve by ID
app.post('/users',         create);   // Create โ€” add a new resource
app.put('/users/:id',      replace);  // Update โ€” full replacement
app.patch('/users/:id',    update);   // Update โ€” partial update
app.delete('/users/:id',   remove);   // Delete โ€” remove a resource
app.options('/users',      options);  // CORS preflight
app.head('/users',         head);     // Like GET but no response body

// app.all() โ€” matches ALL HTTP methods
app.all('/secret', (req, res, next) => {
  console.log('Any method hit /secret');
  next();
});

// Route chaining โ€” same path, different 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) => res.json(deleteUser(req.params.id)));

PUT vs PATCH: PUT replaces the entire resource (all fields required). PATCH updates only the fields provided (partial update). REST APIs typically support both, with PATCH being preferred for partial updates.

07 What is the Express Router? How do you use it to organise routes?

Routing express.Router() creates a mini-application capable of performing middleware and routing. It is the standard way to organise routes into separate files.

// routes/users.js โ€” user-specific routes
const router = require('express').Router();

router.get('/',    (req, res) => res.json(getAllUsers()));
router.get('/:id', (req, res) => res.json(getUserById(req.params.id)));
router.post('/',   (req, res) => res.status(201).json(createUser(req.body)));
router.put('/:id', (req, res) => res.json(updateUser(req.params.id, req.body)));
router.delete('/:id', (req, res) => res.json(deleteUser(req.params.id)));

module.exports = router;

// routes/products.js โ€” product routes
const router = require('express').Router();
router.get('/', (req, res) => res.json(getProducts()));
module.exports = router;

// app.js โ€” mount routers with path prefixes
const usersRouter   = require('./routes/users');
const productsRouter = require('./routes/products');

app.use('/api/users',    usersRouter);    // /api/users, /api/users/:id
app.use('/api/products', productsRouter); // /api/products

Router-level middleware applied to router.use() runs only for routes in that router โ€” enabling auth checks scoped to specific resources.

08 What is the res object in Express? What are the key response methods?

Core The res (response) object represents the HTTP response. Express extends Node’s http.ServerResponse with convenient helper methods.

// Send JSON โ€” sets Content-Type: application/json automatically
res.json({ message: 'Success', data: users });

// Send plain text or HTML
res.send('Hello World');
res.send('<h1>Home Page</h1>');

// Set HTTP status code
res.status(201).json({ id: newUser.id });
res.status(404).json({ error: 'User not found' });
res.status(204).send(); // 204 No Content โ€” no body

// Redirect
res.redirect('/login');
res.redirect(301, 'https://new-url.com');

// Send a file for download
res.download('./reports/report.pdf', 'monthly-report.pdf');

// Render a template (with a view engine like EJS or Pug)
res.render('profile', { user: req.user });

// Set custom headers
res.set('X-Custom-Header', 'value');
res.set({ 'Content-Type': 'application/json', 'X-Version': '1.0' });

// Send a cookie
res.cookie('token', jwt, { httpOnly: true, secure: true, maxAge: 86400000 });

09 How does error handling work in Express?

Error Handling Express uses a special four-argument middleware for centralised error handling: (err, req, res, next). This middleware must be registered after all routes and other middleware.

// Passing an error to the error handler
app.get('/users/:id', async (req, res, next) => {
  try {
    const user = await User.findById(req.params.id);
    if (!user) return next(createError(404, 'User not found')); // custom error
    res.json(user);
  } catch (err) {
    next(err); // forward to error handler
  }
});

// Synchronous errors can also be thrown โ€” Express catches them automatically
// (but only in synchronous route handlers, NOT in async functions pre-Express 5)

// โ”€โ”€ Centralised error handler โ€” register LAST โ”€โ”€
app.use((err, req, res, next) => {
  const status  = err.status || err.statusCode || 500;
  const message = err.message || 'Internal Server Error';

  // Log in development, omit stack in production
  if (process.env.NODE_ENV === 'development') console.error(err.stack);

  res.status(status).json({
    error: { message, status }
  });
});

// 404 handler โ€” catch all unmatched routes (register before error handler)
app.use((req, res, next) => {
  next(createError(404, `Route ${req.url} not found`));
});

10 How do you serve static files in Express?

Core The built-in express.static() middleware serves static assets (HTML, CSS, JS, images, fonts) from a directory.

// Serve files from the "public" directory
app.use(express.static('public'));
// GET /style.css     โ†’ serves public/style.css
// GET /images/logo.png โ†’ serves public/images/logo.png

// With a virtual path prefix
app.use('/static', express.static('public'));
// GET /static/style.css โ†’ serves public/style.css

// Using an absolute path (recommended โ€” works regardless of where Node is started)
const path = require('path');
app.use(express.static(path.join(__dirname, 'public')));

// Multiple static directories (searched in order)
app.use(express.static('public'));
app.use(express.static('uploads'));

// Serve a single-page app (SPA) โ€” send index.html for all unmatched GET routes
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

Static middleware bypasses dynamic route handlers entirely for matched files โ€” it is very efficient. Set cache headers with the maxAge option: express.static('public', { maxAge: '1d' }).

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

Routing

  • app.use(path, fn) โ€” matches the beginning of the URL. Responds to ALL HTTP methods. Primarily used for mounting middleware and sub-routers. The path defaults to '/' (matches every request).
  • app.get(path, fn) โ€” matches the exact URL path (plus route parameters). Responds only to GET requests. Used for defining specific route handlers.
// app.use('/api') matches: /api, /api/users, /api/users/1, /api/anything
app.use('/api', apiRouter);

// app.get('/api') matches ONLY: /api (exact, GET method only)
app.get('/api', (req, res) => res.json({ version: '1.0' }));

// Practical difference:
app.use('/admin', adminRouter);     // mounts router for all /admin/* routes
app.use(express.json());            // applies to ALL routes, all methods
app.get('/health', healthCheck);    // only GET /health, exact match

A common beginner mistake: using app.use() for a route that should only respond to one HTTP method, allowing unintended access via POST, PUT, DELETE, etc.

12 What is CORS and how do you enable it in Express?

Security CORS (Cross-Origin Resource Sharing) is a browser security mechanism that blocks JavaScript in one origin (domain) from making requests to a different origin unless the server explicitly allows it via response headers.

npm install cors
const cors = require('cors');

// Allow ALL origins (development only โ€” too permissive for production)
app.use(cors());

// Allow specific origin
app.use(cors({ origin: 'https://myapp.com' }));

// Multiple origins
const allowedOrigins = ['https://myapp.com', 'https://admin.myapp.com'];
app.use(cors({
  origin: (origin, callback) => {
    if (!origin || allowedOrigins.includes(origin)) callback(null, true);
    else callback(new Error('CORS not allowed for this origin'));
  },
  methods:          ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
  allowedHeaders:   ['Content-Type', 'Authorization'],
  credentials:      true,          // allow cookies/auth headers
  maxAge:           86400          // preflight cache: 24 hours
}));

// Enable CORS for a single route
app.get('/public-data', cors(), (req, res) => res.json(data));

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

Request

  • req.params โ€” dynamic segments defined in the route path with :. Part of the URL path itself. Always strings.
  • req.query โ€” key-value pairs after ? in the URL. Used for filtering, sorting, pagination. Always strings (parse numbers explicitly).
  • req.body โ€” data sent in the HTTP request body. Requires a body-parsing middleware (express.json() or express.urlencoded()). Used for POST/PUT/PATCH payloads.
// Route: GET /orders/:orderId/items?status=pending&page=2
app.get('/orders/:orderId/items', (req, res) => {
  console.log(req.params);  // { orderId: "123" }
  console.log(req.query);   // { status: "pending", page: "2" }
});

// Route: POST /orders  body: { "item": "Widget", "qty": 3 }
app.post('/orders', (req, res) => {
  console.log(req.body);    // { item: "Widget", qty: 3 }
});

Security note: Never trust user-supplied values in req.params, req.query, or req.body without validation. Always sanitise and validate inputs to prevent injection attacks.

14 What is the next() function in Express middleware?

Middleware The next() function passes control to the next matching middleware or route handler in the stack. It is the mechanism Express uses to chain middleware together.

// next() โ€” continue to the next middleware
function logger(req, res, next) {
  console.log(req.method, req.url);
  next(); // pass control downstream
}

// next(err) โ€” skip to the error handler
function authMiddleware(req, res, next) {
  const token = req.headers.authorization;
  if (!token) return next(new Error('Unauthorised')); // skip all regular middleware
  req.user = verifyToken(token);
  next();
}

// next('route') โ€” skip remaining handlers in the CURRENT route, jump to next route
app.get('/users/:id',
  (req, res, next) => {
    if (req.params.id === 'me') return next('route'); // skip to next matching route
    res.json(getUserById(req.params.id));
  }
);
app.get('/users/me', (req, res) => res.json(req.user)); // handles /users/me

Common mistake: Calling next() after sending a response (res.json()) causes “headers already sent” errors. Always use return next() or ensure next() is only called when you haven’t already sent a response.

15 How do you handle async/await in Express route handlers?

Async Express 4.x does not catch rejected Promises automatically โ€” you must wrap async functions in try/catch and pass errors to next(). Express 5 (currently in beta) handles this automatically.

// Express 4 โ€” manual try/catch required
app.get('/users/:id', async (req, res, next) => {
  try {
    const user = await User.findById(req.params.id);
    if (!user) return res.status(404).json({ error: 'Not found' });
    res.json(user);
  } catch (err) {
    next(err); // forward to error handler โ€” ESSENTIAL
  }
});

// Utility wrapper โ€” avoids try/catch boilerplate on every route
const asyncHandler = fn => (req, res, next) =>
  Promise.resolve(fn(req, res, next)).catch(next);

// Usage โ€” clean and concise
app.get('/users/:id', asyncHandler(async (req, res) => {
  const user = await User.findById(req.params.id);
  if (!user) return res.status(404).json({ error: 'Not found' });
  res.json(user);
}));

// Or install: npm install express-async-handler
// const asyncHandler = require('express-async-handler');

Express 5 makes asyncHandler unnecessary โ€” it natively catches async errors and passes them to next(err).

16 What are environment variables and how do you use them in Express?

Config Environment variables store configuration that changes between environments (development, staging, production): API keys, database URLs, ports, secrets. Never hardcode these in source code.

npm install dotenv
# .env file (add to .gitignore โ€” never commit secrets)
PORT=3000
NODE_ENV=development
MONGODB_URI=mongodb://localhost:27017/myapp
JWT_SECRET=super-secret-key-change-in-production
ALLOWED_ORIGIN=http://localhost:4200
// Load .env at the very start of your app (before any other requires)
require('dotenv').config();

const app = express();
const PORT = process.env.PORT || 3000;
const MONGO_URI = process.env.MONGODB_URI;
const JWT_SECRET = process.env.JWT_SECRET;

// Check required variables exist
if (!JWT_SECRET) throw new Error('JWT_SECRET environment variable is required');

app.listen(PORT, () => console.log(`Running on port ${PORT}` ));

In production (Heroku, Railway, AWS, Vercel), set environment variables through the platform’s dashboard or CLI โ€” never use a .env file in production containers.

17 What is the express.json() and express.urlencoded() middleware?

Middleware These are built-in body-parsing middleware (included since Express 4.16 โ€” previously the separate body-parser package).

  • express.json() โ€” parses requests with Content-Type: application/json and populates req.body with the parsed JavaScript object. Used for REST API JSON payloads.
  • express.urlencoded({ extended: true }) โ€” parses requests with Content-Type: application/x-www-form-urlencoded. Used for traditional HTML form submissions.
app.use(express.json());                        // for JSON APIs
app.use(express.urlencoded({ extended: true })); // for HTML forms

// extended: true  โ€” uses the 'qs' library (supports nested objects: a[b]=1)
// extended: false โ€” uses Node's built-in 'querystring' (flat key-value only)

// Limit body size (protect against large payload attacks)
app.use(express.json({ limit: '10kb' }));
app.use(express.urlencoded({ extended: true, limit: '10kb' }));

// Without these middleware, req.body is undefined
app.post('/submit', (req, res) => {
  console.log(req.body); // undefined without body parser โŒ
});

18 How do you implement a basic REST API with Express?

REST

const express = require('express');
const router = express.Router();
let users = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];
let nextId = 3;

// GET /api/users โ€” list all
router.get('/', (req, res) => {
  res.json(users);
});

// GET /api/users/:id โ€” get one
router.get('/:id', (req, res) => {
  const user = users.find(u => u.id === Number(req.params.id));
  if (!user) return res.status(404).json({ error: 'User not found' });
  res.json(user);
});

// POST /api/users โ€” create
router.post('/', (req, res) => {
  const { name } = req.body;
  if (!name) return res.status(400).json({ error: 'Name is required' });
  const user = { id: nextId++, name };
  users.push(user);
  res.status(201).json(user);
});

// PUT /api/users/:id โ€” replace
router.put('/:id', (req, res) => {
  const idx = users.findIndex(u => u.id === Number(req.params.id));
  if (idx === -1) return res.status(404).json({ error: 'Not found' });
  users[idx] = { id: users[idx].id, ...req.body };
  res.json(users[idx]);
});

// DELETE /api/users/:id โ€” remove
router.delete('/:id', (req, res) => {
  users = users.filter(u => u.id !== Number(req.params.id));
  res.status(204).send();
});

module.exports = router;

19 What is the morgan middleware and why is logging important?

Tooling morgan is an HTTP request logger middleware for Express. It logs details about each request: method, URL, status code, response time, and response size.

npm install morgan
const morgan = require('morgan');

// Predefined formats
app.use(morgan('dev'));     // concise colourised output for development
app.use(morgan('tiny'));    // minimal output
app.use(morgan('combined')); // Apache Combined Log Format (for production)

// Example dev output:
// GET /api/users 200 12.345 ms - 456

// Custom token โ€” log authenticated user
morgan.token('user', req => req.user?.id || 'anonymous');
app.use(morgan(':method :url :status :response-time ms - user::user'));

// Log only errors in production (avoid noise)
app.use(morgan('combined', {
  skip: (req, res) => res.statusCode < 400
}));

// Write logs to a file (rotating log files)
const rfs = require('rotating-file-stream');
const accessLogStream = rfs.createStream('access.log', { interval: '1d', path: './logs' });
app.use(morgan('combined', { stream: accessLogStream }));

20 How do you structure a production Express application?

Architecture A well-organised Express application separates concerns into clear layers:

my-api/
  src/
    config/           # DB connection, env validation, constants
      db.js
      env.js
    middleware/       # Custom middleware functions
      auth.js
      validate.js
      rateLimiter.js
    routes/           # Route definitions โ€” thin, delegate to controllers
      users.routes.js
      products.routes.js
    controllers/      # Request/response handling โ€” orchestrates services
      users.controller.js
    services/         # Business logic โ€” pure functions, no req/res
      users.service.js
    models/           # Database models/schemas (Mongoose, Sequelize)
      user.model.js
    utils/            # Helper functions, error classes
      AppError.js
      asyncHandler.js
    app.js            # Express app setup (middleware + routes)
    server.js         # HTTP server startup (listen)
  tests/
  .env
  .env.example        # Committed โ€” shows required env vars without values
  package.json

Key separation: Routes handle URL mapping only. Controllers handle HTTP (req/res). Services contain pure business logic with no HTTP awareness โ€” making them easily testable and reusable.

21 What is the difference between res.send() and res.json()?

Response

  • res.send(body) โ€” sends the response body. Automatically sets Content-Type based on the argument type: String โ†’ text/html, Buffer โ†’ application/octet-stream, Object/Array โ†’ calls res.json(). Also sets ETag and Content-Length.
  • res.json(body) โ€” always sends JSON. Sets Content-Type: application/json. Converts the value to a JSON string using JSON.stringify(). Also handles special values like null correctly (which res.send(null) would mishandle).
res.send('Hello');          // Content-Type: text/html
res.send({ name: 'Alice' }); // Content-Type: application/json (delegates to json)
res.json({ name: 'Alice' }); // Content-Type: application/json (explicit, preferred)
res.json(null);              // sends "null" โ€” valid JSON
res.send(null);              // sends empty body โ€” probably not intended

// For REST APIs, always use res.json() for object/array responses
// For file streams, HTML pages, or plain text, use res.send()

22 What is Helmet.js and why should you use it in production?

Security Helmet is a collection of middleware functions that set security-related HTTP response headers. It protects against many common web vulnerabilities with almost zero configuration.

npm install helmet
const helmet = require('helmet');
app.use(helmet()); // applies all 14+ security headers with sensible defaults

// What helmet sets (and why):
// Content-Security-Policy    โ€” prevent XSS attacks
// X-Frame-Options: DENY       โ€” prevent clickjacking (no iframes)
// X-Content-Type-Options     โ€” prevent MIME sniffing
// Strict-Transport-Security  โ€” force HTTPS (HSTS)
// X-XSS-Protection           โ€” legacy XSS filter (deprecated but harmless)
// Referrer-Policy            โ€” control referrer header leakage
// Permissions-Policy         โ€” restrict browser feature access (camera, mic)
// Cross-Origin-Opener-Policy โ€” isolate browsing context

// Customise individual headers
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc:  ["'self'", "cdn.jsdelivr.net"]
    }
  },
  crossOriginEmbedderPolicy: false  // disable if embedding third-party iframes
}));

Helmet should be the first middleware in your chain so security headers are applied to all responses, including errors. It takes 30 seconds to add and dramatically reduces your attack surface.

📝 Knowledge Check

Test your understanding of Express.js fundamentals with these five questions.

🧠 Quiz Question 1 of 5

What is the correct signature for an Express error-handling middleware function?





🧠 Quiz Question 2 of 5

Which Express property gives you access to dynamic URL segments defined with a colon, such as /users/:id?





🧠 Quiz Question 3 of 5

Which middleware must be registered before route handlers to enable req.body for JSON payloads?





🧠 Quiz Question 4 of 5

What happens if a middleware function neither calls next() nor sends a response?





🧠 Quiz Question 5 of 5

What is the primary purpose of express.Router() in an Express application?