You're reading for free via habtesoft's Friend Link. Become a member to access the best of Medium.

Member-only story

10 Node.js Tips You Won’t Learn in a Beginner’s Guide

habtesoft
4 min readNov 12, 2024

Node.js has become one of the most popular environments for backend development, thanks to its speed, efficiency, and flexibility. However, moving beyond beginner-level Node.js requires mastering more advanced techniques and adopting best practices that can make your applications faster, more maintainable, and scalable. Here are ten essential tips to level up your Node.js skills.

Not a Medium member? Read this article here

1. Use async/await with Caution in Loops

While async/await can simplify asynchronous code, using it in loops can slow down your application. When you use await inside a loop, each iteration waits for the previous one to complete, making it synchronous. Instead, use Promise.all() to run multiple async operations in parallel.

Example:

// Slower: awaits each iteration
for (let userId of userIds) {
await getUserDetails(userId);
}

// Faster: executes all promises in parallel
await Promise.all(userIds.map(id => getUserDetails(id)));

2. Stream Data for Better Performance

Node.js supports streams, which allow you to handle data in chunks rather than loading everything into memory at once. This is particularly useful for handling large files or network requests and improves performance by reducing memory usage.

Example:

const fs = require('fs');
const readStream = fs.createReadStream('largeFile.txt');

readStream.on('data', chunk => {
console.log('New chunk received:', chunk);
});

3. Use Environment Variables Securely

Environment variables are crucial for configuration in Node.js applications. Use libraries like dotenv to load them from a .env file and always exclude sensitive values like API keys or database credentials from your codebase. Never hardcode sensitive information.

Example:

// Load environment variables from a .env file
require('dotenv').config();

const dbPassword = process.env.DB_PASSWORD;

4. Avoid Memory Leaks with Proper Garbage Collection

Memory leaks can creep into Node.js applications if you’re not careful. To minimize leaks, avoid holding onto unused variables and always remove listeners with removeListener or off when they're no longer needed. Profiling tools like Chrome DevTools and node --inspect are great for finding and fixing memory leaks.

Example:

const EventEmitter = require('events');
const emitter = new EventEmitter();

function handler() {
console.log('Event handled');
}

// Add listener
emitter.on('myEvent', handler);

// Remove listener to prevent memory leak
emitter.removeListener('myEvent', handler);

5. Optimize Database Queries

Database queries can often be the bottleneck in an application. Use indexes, avoid SELECT * statements, and optimize your queries. For MongoDB, use indexes effectively and leverage aggregation pipelines where possible. Libraries like Sequelize and Mongoose offer built-in ways to optimize queries.

Example:

// Inefficient query
const users = await User.find({ age: { $gte: 21 } });

// With an index on the age field, this query will be much faster

6. Enable GZIP Compression

GZIP compression can significantly reduce the size of responses sent to clients, improving loading times and bandwidth usage. Use the compression middleware in your Express app to enable GZIP compression.

Example:

const express = require('express');
const compression = require('compression');
const app = express();

app.use(compression()); // Enable compression for all routes

7. Use Cluster Mode to Utilize All CPU Cores

Node.js runs on a single thread by default, but you can take advantage of all CPU cores using clustering. The cluster module creates child processes to handle requests in parallel, which improves performance in high-traffic applications.

Example:

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
for (let i = 0; i < numCPUs; i++) {
cluster.fork(); // Fork workers
}
} else {
http.createServer((req, res) => {
res.writeHead(200);
res.end('Hello World\n');
}).listen(8000);
}

8. Monitor and Handle Uncaught Exceptions

Errors are inevitable, but unhandled exceptions can crash your application. Use error-handling middleware and listen for uncaughtException and unhandledRejection events to log errors and prevent your app from crashing.

Example:

process.on('uncaughtException', (err) => {
console.error('Uncaught Exception:', err);
process.exit(1); // Exit process after logging
});

process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection:', reason);
// Handle or log the rejection here
});

9. Use Caching for Frequent Operations

For data that doesn’t change often (e.g., user sessions or frequently accessed data), caching can reduce database calls and improve performance. Libraries like node-cache or Redis can be used to implement caching in Node.js.

Example:

const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 100, checkperiod: 120 });

// Check cache first
let data = cache.get("myData");
if (!data) {
// If not in cache, fetch from database and set cache
data = await getDataFromDB();
cache.set("myData", data);
}

10. Use pm2 for Process Management

pm2 is a powerful process manager for Node.js that can keep your application alive, restart it if it crashes, and enable load balancing across CPU cores. It’s also helpful for monitoring and automatically managing your app in production.

Installation and usage:

# Install pm2 globally
npm install -g pm2

# Start your app with pm2
pm2 start app.js

# View process logs and status
pm2 status

Moving beyond beginner-level Node.js development requires implementing practices that make your applications faster, safer, and more efficient. By using async/await effectively, managing memory, optimizing database queries, and using tools like pm2 and caching mechanisms, you’ll be on your way to writing production-ready Node.js code. Master these tips, and you’ll be well-prepared to tackle the challenges of real-world Node.js development.

habtesoft
habtesoft

Written by habtesoft

Passionate JavaScript developer with a focus on backend technologies. Always eager to connect and learn. Let’s talk, https://buymeacoffee.com/habtesoftat

Responses (1)

Write a response

Good points.
Be careful that Promise.all() will stop as soon as one of the actions is rejected !
Prefer Promise.allSettled() if you want all your actions to be executed, whether resolved or rejected, and return their statuses.