15 Express.js Interview Questions and Answers
Prepare for your next technical interview with this guide on Express.js, featuring common questions and detailed answers to enhance your skills.
Prepare for your next technical interview with this guide on Express.js, featuring common questions and detailed answers to enhance your skills.
Express.js is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications. Known for its simplicity and performance, Express.js is a popular choice for building RESTful APIs and web applications. Its unopinionated nature allows developers to structure their applications as they see fit, making it a versatile tool in the JavaScript ecosystem.
This article offers a curated selection of interview questions designed to test your knowledge and proficiency with Express.js. By working through these questions and their detailed answers, you will be better prepared to demonstrate your expertise and problem-solving abilities in a technical interview setting.
Middleware functions in Express.js are functions that have access to the request object (req), the response object (res), and the next middleware function in the application’s request-response cycle. These functions can execute code, modify the request and response objects, end the request-response cycle, and call the next middleware function. They are used for tasks such as logging, authentication, and error handling and can be applied globally or to specific routes.
Example:
const express = require('express'); const app = express(); // Global middleware function app.use((req, res, next) => { console.log(`${req.method} request for '${req.url}'`); next(); }); // Route-specific middleware function const checkAuth = (req, res, next) => { if (req.headers['authorization']) { next(); } else { res.status(403).send('Forbidden'); } }; app.get('/secure', checkAuth, (req, res) => { res.send('This is a secure route'); }); app.get('/', (req, res) => { res.send('Hello, world!'); }); app.listen(3000, () => { console.log('Server is running on port 3000'); });
Performance optimization in Express.js applications can be achieved through several techniques:
Session management in Express.js is typically handled using middleware such as express-session
. Sessions allow you to store user data between HTTP requests, which is useful for maintaining user state and authentication.
To manage sessions, set up the express-session
middleware and configure it with options such as the secret key, resave, and saveUninitialized. You can also use a session store to persist session data, such as connect-mongo
for MongoDB or connect-redis
for Redis.
Example:
const express = require('express'); const session = require('express-session'); const MongoStore = require('connect-mongo'); const app = express(); app.use(session({ secret: 'your_secret_key', resave: false, saveUninitialized: true, store: MongoStore.create({ mongoUrl: 'mongodb://localhost/session-db' }) })); app.get('/', (req, res) => { if (req.session.views) { req.session.views++; res.send(`Number of views: ${req.session.views}`); } else { req.session.views = 1; res.send('Welcome to the session demo. Refresh!'); } }); app.listen(3000, () => { console.log('Server is running on port 3000'); });
CORS (Cross-Origin Resource Sharing) is a security feature implemented by web browsers to control how resources on a web page can be requested from another domain. It uses additional HTTP headers to tell browsers to give a web application running at one origin access to selected resources from a different origin.
To configure CORS in an Express.js application, use the cors
middleware. This middleware allows you to specify which domains are permitted to access resources on your server.
Example:
const express = require('express'); const cors = require('cors'); const app = express(); const corsOptions = { origin: 'http://example.com', // specify the allowed origin optionsSuccessStatus: 200 // some legacy browsers choke on 204 }; app.use(cors(corsOptions)); app.get('/data', (req, res) => { res.json({ message: 'This is CORS-enabled for only http://example.com.' }); }); app.listen(3000, () => { console.log('Server is running on port 3000'); });
To create custom middleware in Express.js, define a function that takes three parameters: req
, res
, and next
. The req
object represents the request, the res
object represents the response, and the next
function is used to pass control to the next middleware function in the stack.
Example:
const express = require('express'); const app = express(); // Custom middleware function const customMiddleware = (req, res, next) => { console.log(`Request Method: ${req.method}, Request URL: ${req.url}`); next(); // Pass control to the next middleware function }; // Use the custom middleware app.use(customMiddleware); app.get('/', (req, res) => { res.send('Hello, world!'); }); app.listen(3000, () => { console.log('Server is running on port 3000'); });
In Express.js, different authentication strategies can be implemented using middleware. Passport.js is a popular library for handling authentication, supporting various mechanisms, including local authentication, OAuth, and OpenID.
To implement different authentication strategies, you need to:
Example:
const express = require('express'); const passport = require('passport'); const LocalStrategy = require('passport-local').Strategy; const app = express(); // Configure Passport.js passport.use(new LocalStrategy( function(username, password, done) { // Replace with your user authentication logic if (username === 'user' && password === 'pass') { return done(null, { id: 1, username: 'user' }); } else { return done(null, false, { message: 'Incorrect credentials.' }); } } )); // Initialize Passport.js app.use(passport.initialize()); // Define a login route app.post('/login', passport.authenticate('local', { failureRedirect: '/login' }), function(req, res) { res.redirect('/'); } ); app.listen(3000, () => { console.log('Server is running on port 3000'); });
API rate limiting in Express.js can be implemented using middleware to control the number of requests a client can make to the server within a specific time frame. This helps prevent abuse and ensures fair usage.
A popular library for implementing rate limiting is express-rate-limit
. This middleware allows you to set up rate limiting rules easily.
Example:
const express = require('express'); const rateLimit = require('express-rate-limit'); const app = express(); // Define the rate limit rule const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // limit each IP to 100 requests per windowMs message: 'Too many requests from this IP, please try again later.' }); // Apply the rate limit rule to all requests app.use(limiter); app.get('/', (req, res) => { res.send('Hello, world!'); }); app.listen(3000, () => { console.log('Server is running on port 3000'); });
Unit testing in Express.js involves testing individual components of your application to ensure they work as expected. Mocha is a popular testing framework for Node.js, and Chai is an assertion library that can be used alongside Mocha to write expressive tests.
Example:
// app.js const express = require('express'); const app = express(); app.get('/hello', (req, res) => { res.status(200).send('Hello, world!'); }); module.exports = app; // test/app.test.js const request = require('supertest'); const app = require('../app'); const { expect } = require('chai'); describe('GET /hello', () => { it('should return Hello, world!', (done) => { request(app) .get('/hello') .expect(200) .end((err, res) => { if (err) return done(err); expect(res.text).to.equal('Hello, world!'); done(); }); }); });
In this example, we have a simple Express.js application with a single route /hello
that returns “Hello, world!”. The unit test checks if this route returns the expected response.
When working with Express.js, it is important to follow security best practices to protect your application from common vulnerabilities. Here are some key security best practices:
In a microservices architecture, an application is divided into small, independent services that communicate with each other through APIs. Each service is responsible for a specific piece of functionality and can be developed, deployed, and scaled independently. When using Express.js to structure an application within a microservices architecture, consider the following key points:
Example of a simple Express.js microservice:
const express = require('express'); const app = express(); const port = 3000; app.get('/service', (req, res) => { res.send('This is a microservice response'); }); app.listen(port, () => { console.log(`Microservice listening at http://localhost:${port}`); });
Handling file uploads in Express.js typically involves using middleware to process the uploaded files. One of the most popular middleware for this purpose is multer
, which makes it easy to handle multipart/form-data.
First, install multer
:
npm install multer
Then, set up multer
in your Express application to handle file uploads:
const express = require('express'); const multer = require('multer'); const app = express(); // Set up storage configuration const storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, 'uploads/'); }, filename: function (req, file, cb) { cb(null, file.originalname); } }); const upload = multer({ storage: storage }); // Define a route to handle file uploads app.post('/upload', upload.single('file'), (req, res) => { res.send('File uploaded successfully'); }); app.listen(3000, () => { console.log('Server started on port 3000'); });
In this example, multer
is configured to store uploaded files in the uploads/
directory with their original filenames. The upload.single('file')
middleware is used to handle single file uploads, and the route /upload
processes the uploaded file and sends a success response.
Logging is an important aspect of any Express.js application as it helps in monitoring and debugging by keeping track of various events and errors. Implementing logging can be done using middleware. One of the most popular logging libraries for Express.js is morgan
.
To implement logging, you can follow these steps:
morgan
library using npm.morgan
in your Express.js application.Example:
const express = require('express'); const morgan = require('morgan'); const app = express(); // Use morgan to log requests to the console app.use(morgan('combined')); app.get('/', (req, res) => { res.send('Hello, world!'); }); app.listen(3000, () => { console.log('Server is running on port 3000'); });
In this example, morgan
is configured to use the ‘combined’ predefined format, which provides detailed logging information including the remote address, request method, URL, HTTP version, response status, and more.
Data validation and sanitization are important steps in ensuring that the data received by your Express.js application is both correct and safe. Validation checks if the data meets certain criteria, while sanitization cleans the data to prevent malicious input.
In Express.js, these tasks are often handled using middleware. One popular library for this purpose is express-validator
, which provides a set of validation and sanitization middlewares.
Example:
const express = require('express'); const { body, validationResult } = require('express-validator'); const app = express(); app.use(express.json()); app.post('/user', // Validation and sanitization middleware [ body('username').isAlphanumeric().trim().escape(), body('email').isEmail().normalizeEmail(), body('password').isLength({ min: 5 }).trim().escape() ], (req, res) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); } // Proceed with the request if validation passes res.send('User data is valid and sanitized'); } ); app.listen(3000, () => { console.log('Server is running on port 3000'); });
Role-based access control (RBAC) is a method of regulating access to resources based on the roles of individual users within an organization. In an Express.js application, RBAC can be implemented by defining roles, assigning roles to users, and creating middleware to restrict access to certain routes based on the user’s role.
Example:
const express = require('express'); const app = express(); // Mock user data const users = { alice: { role: 'admin' }, bob: { role: 'user' } }; // Middleware to check user role function checkRole(role) { return (req, res, next) => { const user = users[req.headers.username]; if (user && user.role === role) { next(); } else { res.status(403).send('Forbidden'); } }; } // Routes app.get('/admin', checkRole('admin'), (req, res) => { res.send('Welcome Admin'); }); app.get('/user', checkRole('user'), (req, res) => { res.send('Welcome User'); }); app.listen(3000, () => { console.log('Server is running on port 3000'); });
In this example, we define a simple user data structure with roles and create a middleware function checkRole
to verify the user’s role before granting access to specific routes. The middleware checks the user’s role from the request headers and either allows access or returns a 403 Forbidden status.
Optimizing middleware performance in Express.js involves several strategies and best practices:
compression
to reduce the size of the response body. This decreases the amount of data transmitted over the network, speeding up the response time.express.static
. This middleware is optimized for serving static content and can significantly improve performance.pm2
and New Relic
can provide insights into middleware performance and help in optimizing it.