Nodejs: 100 Interview Questions

Let’s go through each question and provide detailed explanations along with code examples where applicable.

Table of Contents

Node.js Basics

1. What is Node.js and why is it used?

Node.js is a runtime environment built on Chrome’s V8 JavaScript engine that allows developers to run JavaScript on the server side. It is used for building fast, scalable network applications due to its non-blocking, event-driven architecture.

Example:

// hello.js
const http = require('http');
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello, World!\n');
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});

Run the above code with the command:

node hello.js

2. How does Node.js handle child threads?

Node.js uses the child_process module to handle child threads. It can create separate processes to run parallel tasks.

Example:

// child.js
process.on('message', (message) => {
console.log('Message from parent:', message);
process.send({ reply: 'Hello from child' });
});
// parent.js
const { fork } = require('child_process');
const child = fork('./child.js');
child.on('message', (message) => {
console.log('Message from child:', message);
});
child.send({ hello: 'world' });

Run the parent process with:

node parent.js

3. Describe the event-driven programming in Node.js.

Node.js uses an event-driven architecture where the flow of the program is determined by events such as user actions, sensor outputs, or message passing from other programs.

Example:

const EventEmitter = require('events');
const eventEmitter = new EventEmitter();
eventEmitter.on('greet', () => {
console.log('Hello, world!');
});
eventEmitter.emit('greet');

4. What is the event loop in Node.js?

The event loop is a fundamental part of Node.js’s architecture. It allows Node.js to perform non-blocking I/O operations by offloading operations to the system kernel whenever possible.

5. What is the difference between Node.js and traditional web server technologies?

Node.js is non-blocking and event-driven, while traditional web servers like Apache use a blocking, multi-threaded model. Node.js is single-threaded and uses asynchronous operations, which can handle many connections with fewer resources.

6. Explain what “non-blocking” means in Node.js.

Non-blocking means that Node.js can handle multiple requests at the same time without waiting for any of the requests to complete. Instead, it uses callbacks and promises to handle asynchronous operations.

Example:

const fs = require('fs');
fs.readFile('example.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log(data);
});
console.log('This will run before the file is read');

7. How do you update Node.js to the latest version?

You can update Node.js using a version manager like nvm (Node Version Manager).

nvm install node  # Install the latest version
nvm use node      # Use the latest version
nvm alias default node  # Set the latest version as the default

8. What is “npm” and what is it used for?

npm (Node Package Manager) is the default package manager for Node.js. It is used to install, share, and manage dependencies for Node.js projects.

9. How do you manage packages in a Node.js project?

You manage packages in a Node.js project using npm or yarn. You can install packages using the npm install command and specify them in the package.json file.

Example:

npm init -y
npm install express

10. What is a package.json file?

The package.json file is a JSON file that contains metadata about the project and its dependencies. It is used by npm to manage project dependencies and scripts.

Example:

{
"name": "my-project",
"version": "1.0.0",
"description": "A simple Node.js project",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"express": "^4.17.1"
}
}

Node.js Core Modules

11. Describe some of the core modules of Node.js.

Node.js includes several core modules, some of which are:

  • http: Used to create HTTP servers and clients.
  • fs: Provides an API for interacting with the file system.
  • path: Provides utilities for working with file and directory paths.
  • os: Provides operating system-related utility methods and properties.
  • events: Provides an EventEmitter class to handle events.
  • stream: Provides an API for working with streaming data.
  • util: Provides utility functions for various tasks.

12. How do you create a simple server in Node.js using the HTTP module?

Example:

const http = require('http');
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello, World!\n');
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});

Run the above code with the command:

node server.js

13. Explain the purpose of the File System (fs) module.

The fs module provides an API to interact with the file system, allowing for reading, writing, updating, deleting, and manipulating files and directories.

14. What is the Buffer class in Node.js?

The Buffer class is used to handle binary data directly in memory. It allows for manipulation of raw binary data, which is useful for working with streams of data, such as file system operations and network operations.

Example:

const buf = Buffer.from('Hello, World!', 'utf8');
console.log(buf.toString('hex'));
console.log(buf.toString('utf8'));

15. What are streams in Node.js and what types are available?

Streams are objects that allow reading and writing data in a continuous flow. They are used to handle large amounts of data efficiently.

Types of streams:

  • Readable: Stream from which data can be read.
  • Writable: Stream to which data can be written.
  • Duplex: Stream that is both readable and writable.
  • Transform: Duplex stream that can modify or transform the data as it is written and read.

16. How do you read and write files in Node.js?

Example:

const fs = require('fs');
// Write to a file
fs.writeFile('example.txt', 'Hello, world!', (err) => {
if (err) throw err;
console.log('File has been saved!');
// Read the file
fs.readFile('example.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log(data);
});
});

17. How do you use the EventEmitter in Node.js?

Example:

const EventEmitter = require('events');
const eventEmitter = new EventEmitter();
eventEmitter.on('greet', () => {
console.log('Hello, world!');
});
eventEmitter.emit('greet');

18. What is the QueryString module?

The QueryString module provides utilities for parsing and formatting URL query strings.

Example:

const querystring = require('querystring');
const parsed = querystring.parse('name=John&age=30');
console.log(parsed);
const stringified = querystring.stringify({ name: 'John', age: 30 });
console.log(stringified);

19. How do you manage path operations in Node.js?

The path module provides utilities for working with file and directory paths.

Example:

const path = require('path');
const fullPath = path.join('/foo', 'bar', 'baz/asdf', 'quux', '..');
console.log(fullPath);
const fileName = path.basename('/foo/bar/baz/asdf/quux.html');
console.log(fileName);
const dirName = path.dirname('/foo/bar/baz/asdf/quux.html');
console.log(dirName);
const extName = path.extname('/foo/bar/baz/asdf/quux.html');
console.log(extName);

Networking in Node.js & Error Handling

25. How does Node.js handle HTTP requests and responses?

Node.js handles HTTP requests and responses using the http module. You create a server, listen for incoming requests, and send back responses.

Example:

const http = require('http');
const server = http.createServer((req, res) => {
if (req.method === 'GET' && req.url === '/') {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello, World!');
} else {
res.statusCode = 404;
res.setHeader('Content-Type', 'text/plain');
res.end('Not Found');
}
});
server.listen(3000, () => {
console.log('Server is listening on port 3000');
});

26. What is Express.js and why is it important for Node.js?

Express.js is a minimal and flexible Node.js web application framework that provides a robust set of features to develop web and mobile applications. It simplifies the process of handling HTTP requests and responses and provides middleware support.

27. How do you create a RESTful API with Node.js?

Example using Express.js:

const express = require('express');
const app = express();
const port = 3000;
app.use(express.json());
let items = [
{ id: 1, name: 'Item One' },
{ id: 2, name: 'Item Two' }
];
app.get('/items', (req, res) => {
res.json(items);
});
app.post('/items', (req, res) => {
const newItem = { id: items.length + 1, name: req.body.name };
items.push(newItem);
res.status(201).json(newItem);
});
app.put('/items/:id', (req, res) => {
const item = items.find(i => i.id == req.params.id);
if (item) {
item.name = req.body.name;
res.json(item);
} else {
res.status(404).send('Item not found');
}
});
app.delete('/items/:id', (req, res) => {
const index = items.findIndex(i => i.id == req.params.id);
if (index !== -1) {
items.splice(index, 1);
res.status(204).send();
} else {
res.status(404).send('Item not found');
}
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});

28. What is middleware in the context of Node.js?

Middleware functions 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. Middleware can:

  • Execute any code.
  • Make changes to the request and response objects.
  • End the request-response cycle.
  • Call the next middleware function in the stack.

Example:

const express = require('express');
const app = express();
const logger = (req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
};
app.use(logger);
app.get('/', (req, res) => {
res.send('Hello, World!');
});
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});

29. How do you ensure security in HTTP headers with Node.js?

You can ensure security in HTTP headers using middleware such as helmet in Express.js, which sets various HTTP headers to help protect your app.

Example:

const express = require('express');
const helmet = require('helmet');
const app = express();
app.use(helmet());
app.get('/', (req, res) => {
res.send('Hello, World!');
});
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});

30. How do you handle errors in Node.js?

Error handling in Node.js can be done using try-catch blocks, error-first callback patterns, or middleware in Express.js.

Example with try-catch:

try {
const data = fs.readFileSync('/file-does-not-exist');
} catch (err) {
console.error('Error reading file:', err);
}

31. Describe some error first callback patterns in Node.js.

Error-first callbacks take an error object as the first argument and the result as the second argument.

Example:

const fs = require('fs');
fs.readFile('example.txt', 'utf8', (err, data) => {
if (err) {
return console.error('Error reading file:', err);
}
console.log(data);
});

32. What are some common debugging techniques for Node.js applications?

  • Using console.log() statements to track values and flow.
  • Using a debugger (e.g., node --inspect-brk).
  • Using tools like Visual Studio Code’s debugging features.
  • Using logging libraries like winston or morgan.

33. Explain process.nextTick().

process.nextTick() defers the execution of a function until the next iteration of the event loop.

Example:

console.log('Start');
process.nextTick(() => {
console.log('Next tick callback');
});
console.log('End');

34. What is the global object in Node.js?

The global object in Node.js is global. It is the equivalent of the window object in browsers.

Example:

global.foo = 'Hello, world!';
console.log(global.foo);

Testing in Node.js & Databases

35. What frameworks are available for testing Node.js applications?

Several frameworks are available for testing Node.js applications, including:

  • Mocha: A feature-rich JavaScript test framework running on Node.js and in the browser.
  • Jest: A delightful JavaScript testing framework with a focus on simplicity.
  • Jasmine: A behavior-driven development framework for testing JavaScript code.
  • Chai: A BDD / TDD assertion library for Node.js.
  • Supertest: A library for testing HTTP servers in Node.js.

36. Explain the concept of mocking in Node.js.

Mocking is a technique used in testing to create a fake implementation of a function or module. This allows you to test code behavior in isolation without relying on external dependencies.

Example with Jest:

const { fetchData } = require('./fetchData');
jest.mock('./fetchData');
test('should fetch data', async () => {
fetchData.mockResolvedValue({ data: 'mock data' });
const data = await fetchData();
expect(data).toEqual({ data: 'mock data' });
});

37. Why is benchmarking important in Node.js?

Benchmarking is important in Node.js to measure the performance of your application, identify bottlenecks, and ensure that your code meets performance requirements. It helps in making informed decisions about optimization and scalability.

38. How do you test an HTTP server in Node.js?

You can test an HTTP server using the supertest library.

Example:

const request = require('supertest');
const express = require('express');
const app = express();
app.get('/test', (req, res) => {
res.status(200).send('Hello, World!');
});
test('GET /test', async () => {
const response = await request(app).get('/test');
expect(response.status).toBe(200);
expect(response.text).toBe('Hello, World!');
});

39. How do you connect a MySQL database with Node.js?

You can connect a MySQL database with Node.js using the mysql or mysql2 library.

Example:

const mysql = require('mysql2');
const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'password',
database: 'test_db'
});
connection.connect((err) => {
if (err) {
console.error('Error connecting to the database:', err);
return;
}
console.log('Connected to the MySQL database');
});
connection.query('SELECT * FROM users', (err, results) => {
if (err) throw err;
console.log(results);
});
connection.end();

### 40. Explain how NoSQL databases like MongoDB can be used with Node.js.



You can use NoSQL databases like MongoDB with Node.js by using libraries such as `mongoose` or the native `mongodb` driver.

Example with Mongoose:


const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test_db', { useNewUrlParser: true, useUnifiedTopology: true });
const userSchema = new mongoose.Schema({
name: String,
age: Number
});
const User = mongoose.model('User', userSchema);
const user = new User({ name: 'John', age: 30 });
user.save((err) => {
if (err) throw err;
console.log('User saved successfully');
User.find({}, (err, users) => {
if (err) throw err;
console.log(users);
mongoose.connection.close();
});
});

41. What’s the role of ORM in Node.js?



ORM (Object-Relational Mapping) libraries in Node.js provide a way to interact with databases using objects instead of raw SQL queries. They abstract the database interactions, making it easier to perform CRUD operations and manage database schemas.

Popular ORMs in Node.js include:
– **Sequelize**: For SQL databases like MySQL, PostgreSQL, SQLite, and MSSQL.
– **TypeORM**: Supports both SQL and NoSQL databases.
– **Mongoose**: Specifically for MongoDB.

Example with Sequelize:


const { Sequelize, DataTypes } = require('sequelize');
const sequelize = new Sequelize('database', 'username', 'password', {
host: 'localhost',
dialect: 'mysql'
});
const User = sequelize.define('User', {
name: {
type: DataTypes.STRING,
allowNull: false
},
age: {
type: DataTypes.INTEGER,
allowNull: false
}
}, {});
sequelize.sync({ force: true }).then(() => {
return User.create({
name: 'Jane',
age: 25
});
}).then((user) => {
console.log(user.toJSON());
return User.findAll();
}).then((users) => {
console.log(users);
sequelize.close();
});

Node.js Performance & Concurency

42. How can you monitor the performance of a Node.js app?

You can monitor the performance of a Node.js app using tools like:

  • Node.js built-in console.time() and console.timeEnd(): For simple timing of code execution.
  • Node.js built-in process.memoryUsage() and process.cpuUsage(): For memory and CPU usage.
  • Profiler tools like clinic.js and node-inspect: For detailed profiling.
  • Application Performance Monitoring (APM) tools: Such as New Relic, AppDynamics, and Datadog.

Example using console.time:

console.time('databaseQuery');
// Simulate a database query
setTimeout(() => {
console.timeEnd('databaseQuery');
}, 1000);

43. What is clustering in Node.js and how does it work?

Clustering in Node.js allows you to create child processes (workers) that share the same server port. This is useful for taking advantage of multi-core systems to handle more connections.

Example:

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
console.log(`Master ${process.pid} is running`);
// Fork workers.
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died`);
});
} else {
http.createServer((req, res) => {
res.writeHead(200);
res.end('Hello, World!\n');
}).listen(8000);
console.log(`Worker ${process.pid} started`);
}

44. How can you prevent memory leaks in a Node.js application?

To prevent memory leaks in a Node.js application, you can:

  • Avoid global variables and use local scope.
  • Manage event listeners properly by removing listeners when they are no longer needed.
  • Use weak references where appropriate, such as WeakMap and WeakSet.
  • Monitor memory usage and identify leaks using tools like node-inspect or clinic.js.

Example of managing event listeners:

const EventEmitter = require('events');
const emitter = new EventEmitter();
const handler = () => {
console.log('Event occurred!');
};
emitter.on('event', handler);
// Later, when the handler is no longer needed
emitter.removeListener('event', handler);

45. Explain the use of the --inspect flag in Node.js.

The --inspect flag is used to enable the Node.js inspector, which allows you to debug your application using Chrome DevTools or other inspector clients.

Example:

node --inspect-brk app.js

This command will start your Node.js application in debug mode and pause execution until a debugger is attached.

46. How does Node.js handle concurrency?

Node.js handles concurrency using an event-driven, non-blocking I/O model. It uses a single-threaded event loop to handle asynchronous operations, allowing multiple operations to be processed concurrently.

47. What is the difference between process and child_process modules?

  • process module: Provides information and control over the current Node.js process. It includes methods for interacting with the runtime environment (e.g., process.env, process.exit()).
  • child_process module: Provides the ability to spawn child processes, which are separate instances of the Node.js runtime. It includes methods for executing commands, forking processes, and handling inter-process communication (e.g., child_process.exec(), child_process.fork()).

Example using child_process:

const { exec } = require('child_process');
exec('ls', (error, stdout, stderr) => {
if (error) {
console.error(`exec error: ${error}`);
return;
}
console.log(`stdout: ${stdout}`);
console.error(`stderr: ${stderr}`);
});

48. How do worker threads work in Node.js?

Worker threads provide a way to run JavaScript code in parallel using multiple threads. This is useful for CPU-intensive tasks.

Example:

const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');
if (isMainThread) {
const worker = new Worker(__filename, {
workerData: { start: 1, end: 10 }
});
worker.on('message', (result) => {
console.log(`Result: ${result}`);
});
worker.on('error', (error) => {
console.error(`Worker error: ${error}`);
});
worker.on('exit', (code) => {
if (code !== 0) {
console.error(`Worker stopped with exit code ${code}`);
}
});
} else {
const { start, end } = workerData;
let result = 0;
for (let i = start; i <= end; i++) {
result += i;
}
parentPort.postMessage(result);
}

Microservices & Security

49. How is Node.js used in microservices architecture?

Node.js is well-suited for microservices architecture due to its lightweight and efficient nature. In a microservices architecture, an application is broken down into smaller, independent services that communicate with each other over APIs. Node.js is used to build these individual services because of its asynchronous, event-driven architecture, which can handle multiple requests efficiently.

Example:
A Node.js microservice for user authentication might expose RESTful APIs to create, authenticate, and manage users. Other microservices, such as order management or inventory, can communicate with the authentication service to verify users.

50. Explain inter-process communication in a Node.js microservice architecture.

Inter-process communication (IPC) in a Node.js microservice architecture can be achieved through various methods, including:

  • HTTP/HTTPS requests: Using RESTful APIs.
  • Message queues: Using tools like RabbitMQ, Apache Kafka, or Redis.
  • gRPC: A high-performance, open-source universal RPC framework.
  • WebSockets: For real-time communication.

Example with HTTP requests:

const express = require('express');
const axios = require('axios');
const app = express();
const port = 3000;
app.get('/data', async (req, res) => {
try {
const response = await axios.get('http://other-microservice/data');
res.json(response.data);
} catch (error) {
res.status(500).send('Error communicating with other microservice');
}
});
app.listen(port, () => {
console.log(`Service listening on port ${port}`);
});

51. What are some common security best practices for Node.js applications?

  • Use HTTPS: Ensure all data is encrypted during transmission.
  • Validate and sanitize inputs: Prevent SQL injection and other attacks by validating and sanitizing user inputs.
  • Use environment variables: Store sensitive information like API keys and database credentials in environment variables.
  • Implement proper authentication and authorization: Use strong authentication methods and manage user roles and permissions effectively.
  • Regularly update dependencies: Keep your dependencies up to date to avoid known vulnerabilities.
  • Use security headers: Use middleware like helmet to set security-related HTTP headers.

52. How would you protect your Node.js application from XSS attacks?

To protect your Node.js application from XSS (Cross-Site Scripting) attacks:

  • Sanitize user input: Use libraries like xss-clean to sanitize user input.
  • Use content security policy (CSP): Set a CSP header to restrict the sources from which content can be loaded.
  • Escape output: Escape any user-generated content before rendering it on the web page.

Example with xss-clean:

const express = require('express');
const xss = require('xss-clean');
const app = express();
app.use(xss());
app.post('/submit', (req, res) => {
const sanitizedInput = req.body.input;
res.send(`Received sanitized input: ${sanitizedInput}`);
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});

53. What are environment variables and how could you use them in Node.js?

Environment variables are variables that are set outside of the application code and are used to configure the application. They are commonly used to store sensitive information like API keys, database connection strings, and other configuration settings.

Example using dotenv:

  1. Install dotenv:
npm install dotenv
  1. Create a .env file:
DATABASE_URL=mongodb://localhost:27017/mydatabase
SECRET_KEY=mysecretkey
  1. Load the environment variables in your Node.js application:
require('dotenv').config();
const mongoose = require('mongoose');
mongoose.connect(process.env.DATABASE_URL, {
useNewUrlParser: true,
useUnifiedTopology: true
});
const secretKey = process.env.SECRET_KEY;
console.log(`Your secret key is: ${secretKey}`);

WebSockets & Development & Version Control

54. What are WebSockets and how do they work with Node.js?

WebSockets are a protocol that allows for full-duplex communication channels over a single TCP connection. They enable real-time data transfer between the client and server, making them suitable for applications like chat, live notifications, and gaming.

55. How do you set up a WebSocket server in Node.js?

You can set up a WebSocket server using the ws library.

Example:

  1. Install the ws library:
npm install ws
  1. Create a WebSocket server:
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => {
console.log('Client connected');
ws.on('message', (message) => {
console.log(`Received: ${message}`);
ws.send(`Hello, you sent -> ${message}`);
});
ws.on('close', () => {
console.log('Client disconnected');
});
});
console.log('WebSocket server is running on ws://localhost:8080');

56. How do you deploy a Node.js application in production?

To deploy a Node.js application in production, follow these steps:

  1. Set up the environment: Ensure the server has Node.js installed.
  2. Install dependencies: Run npm install to install the necessary dependencies.
  3. Set environment variables: Use environment variables for configuration.
  4. Start the application: Use a process manager like PM2 or systemd to keep the application running.
  5. Set up a reverse proxy: Use Nginx or Apache to forward requests to your Node.js application.
  6. Monitor the application: Use monitoring tools to keep track of the application’s performance and errors.

57. What is PM2 and how is it used in Node.js?

PM2 is a process manager for Node.js applications. It ensures that applications stay online and can handle automatic restarts, load balancing, and more.

Example:

  1. Install PM2:
npm install pm2 -g
  1. Start the application with PM2:
pm2 start app.js
  1. Monitor the application:
pm2 monit
  1. Set up PM2 to start on system boot:
pm2 startup
pm2 save

58. Explain how you would use Docker with a Node.js application.

Using Docker with a Node.js application involves creating a Dockerfile to define the container environment.

Example:

  1. Create a Dockerfile:
# Use the official Node.js image
FROM node:14
# Create and change to the app directory
WORKDIR /usr/src/app
# Install app dependencies
COPY package*.json ./
RUN npm install
# Copy app source code
COPY . .
# Expose the port the app runs on
EXPOSE 3000
# Start the app
CMD [ "node", "app.js" ]
  1. Build the Docker image:
docker build -t my-node-app .
  1. Run the Docker container:
docker run -p 3000:3000 -d my-node-app

59. How do you manage versioning of a Node.js API?

API versioning can be managed by including the version number in the URL path or headers.

Example:

const express = require('express');
const app = express();
app.get('/api/v1/users', (req, res) => {
res.json({ message: 'API Version 1' });
});
app.get('/api/v2/users', (req, res) => {
res.json({ message: 'API Version 2' });
});
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});

60. What are semantic versioning (semver) and its importance in Node.js development?

Semantic Versioning (semver) is a versioning scheme that uses a three-part version number: MAJOR.MINOR.PATCH.

  • MAJOR: Incremented for incompatible API changes.
  • MINOR: Incremented for backward-compatible new features.
  • PATCH: Incremented for backward-compatible bug fixes.

It is important in Node.js development to ensure that package dependencies are managed correctly and to avoid breaking changes in applications.

Example:

{
"name": "my-node-app",
"version": "1.0.0",
"dependencies": {
"express": "^4.17.1"
}
}

Advanced Topics & Framework

61. What is the difference between exports and module.exports in Node.js?

  • exports: A shorthand for module.exports that can be used to add properties to the module.exports object.
  • module.exports: The actual object that is returned when another file requires the module.

Example:

// Using exports
exports.greet = function() {
console.log('Hello');
};
// Using module.exports
module.exports = {
greet: function() {
console.log('Hello');
}
};

When using exports, it should be noted that exports is a reference to module.exports. Assigning a new value to exports will break the link.

62. How can you create a simple TCP server in Node.js?

You can create a TCP server using the net module.

Example:

const net = require('net');
const server = net.createServer((socket) => {
socket.write('Echo server\r\n');
socket.pipe(socket);
});
server.listen(1337, '127.0.0.1');
console.log('TCP server is running on tcp://127.0.0.1:1337');

63. What is REPL in Node.js?

REPL stands for Read-Eval-Print Loop. It is an interactive shell that processes Node.js expressions. It allows you to quickly test snippets of JavaScript code.

You can start the REPL by running node in your terminal:

$ node
> const x = 10;
> x * 2
20

64. Explain the role of a reverse proxy with Node.js applications.

A reverse proxy is a server that sits between client devices and a backend server, forwarding client requests to the backend server and returning the server’s responses to the clients. In Node.js applications, reverse proxies can:

  • Improve security by hiding the backend server.
  • Load balance requests across multiple servers.
  • Serve static content.
  • Handle SSL termination.

Example setup using Nginx as a reverse proxy:

server {
listen 80;
server_name example.com;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

65. How do Node.js streams enhance performance?

Node.js streams provide an efficient way to handle large amounts of data by processing it in chunks, rather than loading the entire data into memory. This is particularly useful for I/O operations like reading files or handling HTTP requests.

Types of streams:

  • Readable: For reading data.
  • Writable: For writing data.
  • Duplex: For both reading and writing.
  • Transform: For transforming data while reading and writing.

Example:

const fs = require('fs');
const readable = fs.createReadStream('input.txt');
const writable = fs.createWriteStream('output.txt');
readable.pipe(writable);

66. Describe some popular frameworks and libraries in the Node.js ecosystem.

  • Express.js: A fast, unopinionated, minimalist web framework.
  • Koa.js: A next-generation web framework designed by the team behind Express.js.
  • NestJS: A progressive Node.js framework for building efficient, reliable, and scalable server-side applications.
  • Hapi.js: A rich framework for building applications and services.
  • Socket.io: A library for real-time web applications.
  • Mongoose: An ODM (Object Data Modeling) library for MongoDB and Node.js.
  • Sequelize: A promise-based ORM for Node.js and SQL databases.

67. How is Koa different from Express.js?

Koa is designed to be more modular and lightweight compared to Express.js. It uses async functions and promises to improve error handling and code readability. Koa does not include middleware by default, allowing developers to build applications from scratch by composing various middleware components.

Example:

// Koa
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
ctx.body = 'Hello, Koa';
});
app.listen(3000);

68. What is NestJS and when would you choose it for your Node.js project?

NestJS is a framework for building efficient, scalable Node.js server-side applications. It uses TypeScript and is inspired by Angular, providing an out-of-the-box application architecture that allows for the effortless creation of highly testable, scalable, loosely coupled, and easily maintainable applications.

You would choose NestJS for:

  • Large-scale enterprise applications.
  • Projects requiring a structured and maintainable architecture.
  • Applications that can benefit from TypeScript’s type-checking.

69. What are the benefits of using TypeScript with Node.js?

  • Static typing: Catches errors at compile time rather than runtime.
  • Enhanced IDE support: Provides better code completion, navigation, and refactoring capabilities.
  • Improved code readability and maintainability: Makes it easier to understand and maintain large codebases.
  • Compatibility with JavaScript: TypeScript is a superset of JavaScript, allowing the gradual adoption of TypeScript features.

Example:

// Install TypeScript
npm install -g typescript
// Create a TypeScript file (example.ts)
const greet = (name: string): string => {
return `Hello, ${name}`;
};
console.log(greet('World'));
// Compile TypeScript to JavaScript
tsc example.ts
// Run the compiled JavaScript
node example.js

Third-Party & Technologies

70. How would you integrate a Node.js app with a third-party API?

You can integrate a Node.js app with a third-party API using HTTP client libraries like axios, node-fetch, or the built-in http/https modules.

Example using axios:

  1. Install axios:
npm install axios
  1. Use axios to make a request to a third-party API:
const axios = require('axios');
axios.get('https://api.example.com/data')
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error('Error fetching data:', error);
});

71. What is Socket.IO and how does it work with Node.js?

Socket.IO is a library that enables real-time, bidirectional communication between web clients and servers. It works by using WebSockets and falling back to other techniques for compatibility with older browsers.

Example:

  1. Install socket.io:
npm install socket.io
  1. Create a server using Socket.IO:
const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = socketIo(server);
io.on('connection', (socket) => {
console.log('A user connected');
socket.on('message', (msg) => {
console.log('Message received:', msg);
io.emit('message', msg);
});
socket.on('disconnect', () => {
console.log('User disconnected');
});
});
server.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});

72. Explain how GraphQL can be used with Node.js.

GraphQL is a query language for APIs that allows clients to request specific data. In Node.js, you can use libraries like apollo-server or express-graphql to set up a GraphQL server.

Example with apollo-server:

  1. Install apollo-server and graphql:
npm install apollo-server graphql
  1. Create a GraphQL server:
const { ApolloServer, gql } = require('apollo-server');
const typeDefs = gql`
type Query {
hello: String
}
`;
const resolvers = {
Query: {
hello: () => 'Hello, world!'
}
};
const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => {
console.log(`Server ready at ${url}`);
});

73. How does Node.js interact with frontend frameworks like Angular or React?

Node.js interacts with frontend frameworks like Angular or React by serving as the backend server. It can handle API requests, serve static files, and perform server-side rendering.

Example with React:

  1. Create a simple Express server:
const express = require('express');
const path = require('path');
const app = express();
app.use(express.static(path.join(__dirname, 'client/build')));
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'client/build', 'index.html'));
});
app.listen(5000, () => {
console.log('Server is running on http://localhost:5000');
});
  1. Build your React app and place the build files in the client/build directory.

74. What is server-side rendering and how can it be achieved with Node.js?

Server-side rendering (SSR) is the process of rendering a web page on the server and sending the fully rendered page to the client. This can improve SEO and initial load times.

Example with Next.js (a React framework for SSR):

  1. Create a Next.js app:
npx create-next-app my-next-app
cd my-next-app
npm run dev
  1. Create a page with SSR:
// pages/index.js
import React from 'react';
const Home = ({ data }) => (
<div>
<h1>Server-side Rendered Data</h1>
<p>{data.message}</p>
</div>
);
export async function getServerSideProps() {
const res = await fetch('https://api.example.com/data');
const data = await res.json();
return { props: { data } };
}
export default Home;

75. What are some coding conventions and best practices in Node.js?

  • Use a consistent coding style: Follow a style guide like Airbnb or Google.
  • Modularize your code: Use modules to keep code organized and reusable.
  • Use Promises and async/await: For better readability and error handling.
  • Error handling: Always handle errors using try-catch blocks or error-first callbacks.
  • Use environment variables: For configuration and sensitive information.
  • Write tests: Ensure your code is tested using frameworks like Mocha, Jest, or Jasmine.

76. How do you ensure your Node.js application adheres to the twelve-factor app principles?

The twelve-factor app principles are a set of best practices for building scalable and maintainable web applications. To ensure adherence:

  • Codebase: Keep a single codebase tracked in version control.
  • Dependencies: Explicitly declare and isolate dependencies.
  • Config: Store configuration in the environment.
  • Backing services: Treat backing services as attached resources.
  • Build, release, run: Strictly separate build and run stages.
  • Processes: Execute the app as one or more stateless processes.
  • Port binding: Export services via port binding.
  • Concurrency: Scale out via the process model.
  • Disposability: Maximize robustness with fast startup and graceful shutdown.
  • Dev/prod parity: Keep development, staging, and production as similar as possible.
  • Logs: Treat logs as event streams.
  • Admin processes: Run admin/management tasks as one-off processes.

77. What is code linting and how is it applied in Node.js?

Code linting is the process of using tools to analyze source code for potential errors, stylistic issues, and best practices. In Node.js, linting can be applied using tools like ESLint.

Example:

  1. Install ESLint:
npm install eslint --save-dev
  1. Initialize ESLint:
npx eslint --init
  1. Create

a .eslintrc.js file and configure it:

module.exports = {
"env": {
"browser": true,
"es2021": true,
"node": true
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module"
},
"rules": {
// Add custom rules here
}
};
  1. Add a script to your package.json to run ESLint:
"scripts": {
"lint": "eslint ."
}
  1. Run the linter:
npm run lint

Scalling & Message Queues & Cloud Services

78. What are some strategies for scaling Node.js applications?

  • Clustering: Use the Node.js cluster module to fork multiple worker processes that share the same server port.
  • Load Balancing: Distribute incoming traffic across multiple instances of your application using a load balancer.
  • Microservices: Break down your application into smaller, independent services that can be scaled individually.
  • Horizontal Scaling: Add more server instances to handle increased load.
  • Caching: Use caching mechanisms (e.g., Redis, Memcached) to reduce database load and improve response times.
  • Database Optimization: Optimize database queries, use indexing, and consider read replicas for databases.
  • Asynchronous Processing: Offload heavy or time-consuming tasks to background processes.

79. How do you handle session management in a scaled Node.js application?

  • In-Memory Store: Suitable for single-instance applications but not scalable.
  • Distributed Store: Use a distributed session store like Redis or a database to manage sessions across multiple instances.
  • Stateless Sessions: Use JSON Web Tokens (JWT) for stateless session management, reducing the need for a server-side session store.

Example using express-session with Redis:

const express = require('express');
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
const redis = require('redis');
const app = express();
const redisClient = redis.createClient();
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: 'your-secret-key',
resave: false,
saveUninitialized: false
}));
app.get('/', (req, res) => {
req.session.viewCount = (req.session.viewCount || 0) + 1;
res.send(`You have viewed this page ${req.session.viewCount} times`);
});
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});

80. How does the use of microservices affect the scalability of a Node.js application?

Microservices architecture improves scalability by allowing each service to be scaled independently based on its load and resource requirements. It enables better fault isolation, as issues in one service do not directly affect others. Additionally, microservices can be developed, deployed, and maintained by separate teams, improving overall development efficiency and flexibility.

81. What are message queues and how are they used in Node.js?

Message queues are systems that enable asynchronous communication between different parts of an application or between different applications. They help decouple components and allow for more scalable and resilient architectures.

Common use cases:

  • Task scheduling: Offloading time-consuming tasks to be processed asynchronously.
  • Event-driven architecture: Reacting to events and triggering actions.
  • Load buffering: Smoothing out spikes in load by queuing tasks.

Example with RabbitMQ:

  1. Install amqplib:
npm install amqplib
  1. Use RabbitMQ to send and receive messages:
// sender.js
const amqp = require('amqplib/callback_api');
amqp.connect('amqp://localhost', (err, conn) => {
conn.createChannel((err, ch) => {
const q = 'hello';
const msg = 'Hello World';
ch.assertQueue(q, { durable: false });
ch.sendToQueue(q, Buffer.from(msg));
console.log(" [x] Sent '%s'", msg);
});
});
// receiver.js
const amqp = require('amqplib/callback_api');
amqp.connect('amqp://localhost', (err, conn) => {
conn.createChannel((err, ch) => {
const q = 'hello';
ch.assertQueue(q, { durable: false });
console.log(" [*] Waiting for messages in %s. To exit press CTRL+C", q);
ch.consume(q, (msg) => {
console.log(" [x] Received '%s'", msg.content.toString());
}, { noAck: true });
});
});

83. What is the significance of ZeroMQ in Node.js applications?

ZeroMQ (ØMQ) is a high-performance asynchronous messaging library that provides a message queue but without a dedicated message broker. It allows for various messaging patterns like publish-subscribe, request-reply, and push-pull. ZeroMQ is used for its lightweight nature, high throughput, and flexibility in building complex messaging systems.

Example:

  1. Install zeromq:
npm install zeromq
  1. Use ZeroMQ for a simple publish-subscribe pattern:
// publisher.js
const zmq = require('zeromq');
const sock = zmq.socket('pub');
sock.bindSync('tcp://127.0.0.1:3000');
console.log('Publisher bound to port 3000');
setInterval(() => {
console.log('sending a multipart message envelope');
sock.send(['kitty cats', 'meow!']);
}, 500);
// subscriber.js
const zmq = require('zeromq');
const sock = zmq.socket('sub');
sock.connect('tcp://127.0.0.1:3000');
sock.subscribe('kitty cats');
console.log('Subscriber connected to port 3000');
sock.on('message', (topic, message) => {
console.log('received a message related to:', topic.toString(), 'containing message:', message.toString());
});

84. How do cloud platforms like AWS, Azure, or GCP facilitate Node.js application deployment?

Cloud platforms provide various services and tools to facilitate the deployment, scaling, and management of Node.js applications:

  • AWS: Elastic Beanstalk, EC2, Lambda, ECS, EKS, RDS.
  • Azure: Azure App Service, Azure Functions, AKS, Azure SQL Database.
  • GCP: App Engine, Cloud Functions, GKE, Cloud SQL.

Example deploying to AWS Elastic Beanstalk:

  1. Install the AWS Elastic Beanstalk CLI:
pip install awsebcli
  1. Initialize and deploy the application:
eb init -p node.js my-node-app
eb create my-node-env

85. What is serverless architecture, and how does it relate to Node.js?

Serverless architecture allows developers to build and run applications without managing server infrastructure. Functions are deployed to a cloud provider, and they automatically scale with demand. Node.js is commonly used in serverless functions due to its lightweight and efficient runtime.

Example using AWS Lambda:

  1. Create a Lambda function:
exports.handler = async (event) => {
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
  1. Deploy the function using the AWS CLI:
aws lambda create-function --function-name my-function --runtime nodejs14.x --role <role-arn> --handler index.handler --zip-file fileb://function.zip

Environment Management & CI/CD

86. How can you manage multiple Node.js versions on the same machine?

You can manage multiple Node.js versions on the same machine using Node Version Manager (NVM).

  1. Install NVM:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
source ~/.nvm/nvm.sh
  1. Install a specific Node.js version:
nvm install 14
nvm install 16
  1. Switch between versions:
nvm use 14
nvm use 16

87. What are .env files and how do they work in a Node.js application?

.env files are used to store environment variables. These files help manage configuration settings and sensitive information outside the source code.

Example using dotenv:

  1. Install dotenv:
npm install dotenv
  1. Create a .env file:
DATABASE_URL=mongodb://localhost:27017/mydatabase
SECRET_KEY=mysecretkey
  1. Load environment variables in your Node.js application:
require('dotenv').config();
const mongoose = require('mongoose');
mongoose.connect(process.env.DATABASE_URL, {
useNewUrlParser: true,
useUnifiedTopology: true
});
const secretKey = process.env.SECRET_KEY;
console.log(`Your secret key is: ${secretKey}`);

88. Describe the usage of the config module in Node.js.

The config module helps manage application configuration settings across multiple environments.

  1. Install config:
npm install config
  1. Create a config directory with configuration files (e.g., default.json, production.json):
// config/default.json
{
"database": {
"url": "mongodb://localhost:27017/mydatabase"
},
"secretKey": "defaultsecretkey"
}
// config/production.json
{
"database": {
"url": "mongodb://prod-db-url:27017/mydatabase"
},
"secretKey": "prodsecretkey"
}
  1. Use the configuration in your Node.js application:
const config = require('config');
const mongoose = require('mongoose');
const dbUrl = config.get('database.url');
const secretKey = config.get('secretKey');
mongoose.connect(dbUrl, {
useNewUrlParser: true,
useUnifiedTopology: true
});
console.log(`Your secret key is: ${secretKey}`);

89. What is continuous integration/deployment and how is it implemented for Node.js apps?

Continuous Integration (CI) and Continuous Deployment (CD) are practices that automate the process of building, testing, and deploying applications to ensure rapid and reliable releases.

To implement CI/CD for Node.js apps, you can use services like GitHub Actions, Travis CI, Jenkins, or CircleCI.

Example using GitHub Actions:

  1. Create a .github/workflows/ci.yml file:
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x, 16.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npm test

90. How do you set up a CI/CD pipeline for a Node.js project?

To set up a CI/CD pipeline:

  1. Version Control: Ensure your project is in a version control system like Git.
  2. Continuous Integration: Automate building and testing using a CI service.
  3. Continuous Deployment: Automate the deployment process using a CD service.

Example using Travis CI:

  1. Create a .travis.yml file:
language: node_js
node_js:
- "14"
- "16"
script:
- npm install
- npm test
deploy:
provider: heroku
api_key:
secure: YOUR_HEROKU_API_KEY
app: your-heroku-app-name
on:
repo: your-github-repo

91. How would you troubleshoot a slow running Node.js application?

To troubleshoot a slow running Node.js application:

  • Profile the application: Use tools like clinic.js or node --inspect to identify performance bottlenecks.
  • Monitor resource usage: Check CPU, memory, and I/O usage using tools like top, htop, or pm2.
  • Analyze logs: Look for errors or warnings in application logs.
  • Check dependencies: Ensure all dependencies are up to date and optimized.
  • Optimize code: Refactor inefficient code and reduce blocking operations.
  • Use caching: Implement caching to reduce database load and improve response times.

92. Describe how to handle file uploads in a Node.js application.

To handle file uploads in a Node.js application, you can use middleware like multer.

Example using multer:

  1. Install multer:
npm install multer
  1. Use multer to handle file uploads:
const express = require('express');
const multer = require('multer');
const path = require('path');
const app = express();
const storage = multer.diskStorage({
destination: './uploads/',
filename: (req, file, cb) => {
cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}
});
const upload = multer({ storage });
app.post('/upload', upload.single('file'), (req, res) => {
res.send('File uploaded successfully');
});
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});

93. How would you handle heavy computation tasks in a Node.js application?

To handle heavy computation tasks in a Node.js application:

  • Offload to worker threads: Use the worker_threads module to run heavy tasks in separate threads.
  • Use a message queue: Offload tasks to a message queue and process them asynchronously.
  • Utilize microservices: Break down heavy tasks into smaller microservices that can be scaled independently.
  • Use external services: Offload computation to external services or cloud functions.

Example using worker_threads:

const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');
if (isMainThread) {
const worker = new Worker(__filename, {
workerData: { start: 1, end: 1000000 }
});
worker.on('message', (result) => {
console.log(`Result: ${result}`);
});
worker.on('error', (error) => {
console.error(`Worker error: ${error}`);
});
worker.on('exit', (code) => {
if (code !== 0) {
console.error(`Worker stopped with exit code ${code}`);
}
});
} else {
const { start, end } = workerData;
let result = 0;
for (let i = start; i <= end; i++) {
result += i;
}
parentPort.postMessage(result);
}

IOT & Machine Learning & API

96. How is Node.js used in IoT (Internet of Things)?

Node.js is used in IoT for its lightweight, event-driven, and non-blocking architecture, making it ideal for handling multiple devices and real-time data. Node.js can be used to:

  • Collect and process data from IoT devices.
  • Send commands to IoT devices.
  • Interface with hardware using libraries like johnny-five.
  • Integrate with MQTT, a lightweight messaging protocol often used in IoT.

97. What would you consider when developing a Node.js application for IoT devices?

When developing a Node.js application for IoT devices, consider the following:

  • Scalability: Ensure the application can handle numerous devices and high data throughput.
  • Security: Implement strong security measures to protect data and devices.
  • Connectivity: Use reliable protocols like MQTT or CoAP for communication.
  • Latency: Minimize latency to ensure real-time responsiveness.
  • Error Handling: Implement robust error handling and recovery mechanisms.
  • Resource Constraints: Optimize for devices with limited resources (CPU, memory, battery).

98. Can you use Node.js for machine learning? If so, how?

Yes, you can use Node.js for machine learning, although it is more common to use languages like Python for ML tasks. Node.js can interface with machine learning models or services using libraries or by calling external APIs.

Example using TensorFlow.js:

  1. Install @tensorflow/tfjs:
npm install @tensorflow/tfjs
  1. Use TensorFlow.js in your Node.js application:
const tf = require('@tensorflow/tfjs');
// Create a simple model
const model = tf.sequential();
model.add(tf.layers.dense({ units: 1, inputShape: [1] }));
// Compile the model
model.compile({ loss: 'meanSquaredError', optimizer: 'sgd' });
// Generate some synthetic data for training
const xs = tf.tensor2d([1, 2, 3, 4], [4, 1]);
const ys = tf.tensor2d([1, 3, 5, 7], [4, 1]);
// Train the model
model.fit(xs, ys, { epochs: 10 }).then(() => {
// Use the model to make a prediction
model.predict(tf.tensor2d([5], [1, 1])).print();
});

99. What are some machine learning libraries or tools available for Node.js?

  • TensorFlow.js: For building and training machine learning models in JavaScript.
  • Brain.js: A neural network library in JavaScript.
  • Synaptic: A JavaScript library for creating neural networks.
  • Natural: A general natural language processing (NLP) library for Node.js.
  • ml5.js: A friendly machine learning library built on top of TensorFlow.js.

100. What are best practices for designing RESTful APIs in Node.js?

  • Use proper HTTP methods: GET for retrieval, POST for creation, PUT/PATCH for updating, DELETE for deletion.
  • Use meaningful endpoint names: Use nouns and avoid verbs (e.g., /users instead of /getUsers).
  • Implement pagination: For endpoints returning large lists of data.
  • Use consistent response formats: Ensure all API responses follow a consistent structure.
  • Handle errors gracefully: Return appropriate HTTP status codes and error messages.
  • Secure your API: Use authentication and authorization mechanisms (e.g., JWT).
  • Version your API: Include versioning in your endpoints (e.g., /v1/users).
  • Document your API: Use tools like Swagger or Postman to document your API.

Example using Express.js:

const express = require('express');
const app = express();
const port = 3000;
app.use(express.json());
let users = [
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Doe' }
];
app.get('/api/v1/users', (req, res) => {
res.json(users);
});
app.post('/api/v1/users', (req, res) => {
const newUser = { id: users.length + 1, name: req.body.name };
users.push(newUser);
res.status(201).json(newUser);
});
app.put('/api/v1/users/:id', (req, res) => {
const user = users.find(u => u.id == req.params.id);
if (user) {
user.name = req.body.name;
res.json(user);
} else {
res.status(404).send('User not found');
}
});
app.delete('/api/v1/users/:id', (req, res) => {
const index = users.findIndex(u => u.id == req.params.id);
if (index !== -1) {
users.splice(index, 1);
res.status(204).send();
} else {
res.status(404).send('User not found');
}
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});

Để lại một bình luận