Master Node.js development with these essential tips and best practices used by top developers in Amsterdam.
Modern Node.js development relies heavily on async/await for handling asynchronous operations. This pattern makes your code more readable and easier to debug compared to callbacks or raw promises.
async function fetchUserData(userId) {
try {
const user = await User.findById(userId);
const orders = await Order.find({ userId });
return { user, orders };
} catch (error) {
console.error('Error fetching user:', error);
throw error;
}
}Never hardcode sensitive data or configuration values. Use environment variables with packages like dotenv to manage configuration across different environments securely.
// .env
DATABASE_URL=postgresql://user:pass@localhost/db
API_KEY=your-secret-key
// app.js
require('dotenv').config();
const db = new Database(process.env.DATABASE_URL);
const apiKey = process.env.API_KEY;Create a centralized error handling middleware in Express and use custom error classes to differentiate between operational and programmer errors.
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.isOperational = true;
}
}
app.use((err, req, res, next) => {
const statusCode = err.statusCode || 500;
res.status(statusCode).json({
status: 'error',
message: err.message
});
});TypeScript catches bugs at compile time and improves code documentation. It's especially valuable in larger Node.js applications where type safety prevents runtime errors.
interface User {
id: string;
email: string;
role: 'admin' | 'user';
}
async function createUser(data: Omit<User, 'id'>): Promise<User> {
const user = await db.users.create({
data: { ...data, id: generateId() }
});
return user;
}Use connection pooling, implement caching with Redis, and always index your database columns properly. Avoid N+1 query problems by using eager loading.
// Connection pooling with pg
const pool = new Pool({
max: 20,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
});
// Eager loading with Prisma
const usersWithPosts = await prisma.user.findMany({
include: { posts: true }
});Validate all incoming data at your API boundaries. Use libraries like Joi, Zod, or class-validator to ensure data integrity before processing.
import { z } from 'zod';
const UserSchema = z.object({
email: z.string().email(),
password: z.string().min(8),
age: z.number().min(18).optional()
});
app.post('/users', (req, res) => {
const result = UserSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json(result.error);
}
// Process validated data
});When handling large files or data sets, use Node.js streams to process data in chunks rather than loading everything into memory.
const fs = require('fs');
const { pipeline } = require('stream/promises');
const zlib = require('zlib');
async function compressFile(input, output) {
await pipeline(
fs.createReadStream(input),
zlib.createGzip(),
fs.createWriteStream(output)
);
console.log('File compressed successfully');
}Protect your APIs from abuse and DDoS attacks by implementing rate limiting. Use packages like express-rate-limit for easy setup.
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests
message: 'Too many requests, try again later'
});
app.use('/api', limiter);Follow a clean architecture pattern to separate concerns. Organize code by feature/domain rather than technical role for better scalability.
// Project structure
src/
├── modules/
│ ├── users/
│ │ ├── user.controller.ts
│ │ ├── user.service.ts
│ │ ├── user.repository.ts
│ │ └── user.routes.ts
│ └── orders/
├── shared/
│ ├── middleware/
│ └── utils/
└── app.tsImplement structured logging with Winston or Pino, and use APM tools to monitor application performance. Include correlation IDs for request tracing.
const pino = require('pino');
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
transport: {
target: 'pino-pretty'
}
});
app.use((req, res, next) => {
req.id = crypto.randomUUID();
req.log = logger.child({ requestId: req.id });
next();
});Slashdev.io provides top Node.js developers for your projects in Amsterdam and worldwide.
Hire Node.js Developers