After years of working with NodeJS, I’ve compiled this MASSIVE list of tips and tricks that would have saved me so much time if I’d known them from day one. These aren’t your typical “read the docs” suggestions. These are real, practical tips from someone who’s lived in the trenches. Some might seem obvious to veterans, but they’re absolute gold for beginners and even intermediate developers. So let’s dive in!
JavaScript can be pretty forgiving, which is both good and bad. Using strict mode forces you to write cleaner, more structured code that will save you from mysterious bugs later.
'use strict';
// The rest of your NodeJS code goes here
Code language: JavaScript (javascript)
Without strict mode, you could accidentally create global variables:
username = "john"; // Works but creates a global variable 😱
Code language: JavaScript (javascript)
But with strict mode:
username = "john";
// Throws: ReferenceError: username is not defined
Code language: JavaScript (javascript)
This forces you to declare variables properly:
let username = "john"; // This is the right way
Code language: JavaScript (javascript)
Strict mode catches common coding mistakes and prevents “silent” errors. It’s not just good practice—it’s essential for production code.
The Node REPL is your playground for testing code snippets. To start it, just type node
in your terminal.
$ node
> let x = 10
undefined
> x * 2
20
Code language: JavaScript (javascript)
Unlike many CLI tools, exiting requires a specific command:
> .exit
Code language: CSS (css)
Or the keyboard shortcut Ctrl+C
twice, or Ctrl+D
once.
Pro tip: You can access your command history using the up and down arrow keys. The REPL even supports tab completion for variables and module names!
NodeJS maintains a single instance across all requests by default, unlike PHP or Python in traditional web servers. This means global variables persist between requests and can serve as a simple in-memory cache.
'use strict';
// This is your cache for this module
const cache = {};
exports.getUser = async function(userId) {
// Check cache first
if (cache[userId]) {
return cache[userId];
}
// If not in cache, fetch from database
const user = await database.fetchUser(userId);
// Store in cache
cache[userId] = user;
return user;
};
Code language: JavaScript (javascript)
But be careful! This approach works best for:
For serious production apps, consider dedicated caching solutions like Redis.
Everyone uses console.log()
, but there’s so much more to the console object!
// Different log levels
console.log('Regular info');
console.error('Something went wrong!');
console.warn('Warning: approaching rate limit');
console.info('FYI: process completed');
// Time operations
console.time('dataFetch');
await fetchData();
console.timeEnd('dataFetch'); // Outputs: dataFetch: 123.4ms
// Group related logs
console.group('User Authentication');
console.log('Validating user input');
console.log('Checking credentials');
console.groupEnd();
// Create tables from data
console.table([
{ name: 'John', age: 30 },
{ name: 'Sarah', age: 25 }
]);
Code language: JavaScript (javascript)
And here’s a trick many developers miss—when logging objects, use the comma separator instead of concatenation:
// Don't do this:
console.log("User data: " + user); // Outputs: "User data: [object Object]"
// Do this instead:
console.log("User data:", user); // Outputs user object properly
Code language: JavaScript (javascript)
To capture all console output to a file:
bash$ node app.js > application.log 2>&1
Never, ever use asterisks in production dependency versions:
// This is dangerous in production!
"dependencies": {
"express": "*"
}
// Much safer approach
"dependencies": {
"express": "^4.18.2"
}
Code language: JavaScript (javascript)
Semantic versioning symbols explained:
^4.18.2
: Accept 4.x.x but not 5.0.0 (safest for most cases)~4.18.2
: Accept 4.18.x but not 4.19.0 (stricter)4.18.2
: Only this exact version (strictest)For truly reproducible builds, use a package-lock.json
file and commit it to your repository. This ensures everyone on your team uses exactly the same dependency versions.
Callbacks were the original way to handle async code, but modern NodeJS gives us much better options:
// Old callback approach (avoid when possible)
function getUser(id, callback) {
db.findUser(id, (err, user) => {
if (err) return callback(err);
callback(null, user);
});
}
// Promise-based approach (better)
function getUser(id) {
return new Promise((resolve, reject) => {
db.findUser(id, (err, user) => {
if (err) return reject(err);
resolve(user);
});
});
}
// Async/await approach (best)
async function getUser(id) {
try {
return await db.findUser(id);
} catch (error) {
throw error;
}
}
Code language: JavaScript (javascript)
For handling multiple asynchronous operations:
// Sequential execution
async function processUsers(userIds) {
const results = [];
for (const id of userIds) {
const user = await getUser(id);
results.push(user);
}
return results;
}
// Parallel execution
async function processUsersParallel(userIds) {
const promises = userIds.map(id => getUser(id));
return Promise.all(promises);
}
Code language: JavaScript (javascript)
Error handling in NodeJS is absolutely critical. Unhandled errors can crash your entire application!
// Always catch errors in async functions
async function fetchData() {
try {
const result = await someAsyncOperation();
return result;
} catch (error) {
console.error('Error fetching data:', error);
// Decide whether to throw or return a default value
throw error; // or return defaultValue;
}
}
// For promises
somePromise()
.then(result => {
// Handle success
})
.catch(error => {
// Handle error
})
.finally(() => {
// Clean up resources
});
// Global uncaught exception handler (last resort)
process.on('uncaughtException', (error) => {
console.error('UNCAUGHT EXCEPTION:', error);
// Log the error, notify admins, etc.
// IMPORTANT: The app is in an undefined state, best to exit
process.exit(1);
});
Code language: JavaScript (javascript)
NodeJS’s power comes from its event-driven, non-blocking I/O model. Understanding this is crucial:
console.log('Start');
setTimeout(() => {
console.log('Timeout callback executed');
}, 0);
Promise.resolve().then(() => {
console.log('Promise resolved');
});
console.log('End');
// Output:
// Start
// End
// Promise resolved
// Timeout callback executed
Code language: JavaScript (javascript)
This happens because:
This is why NodeJS can handle thousands of connections simultaneously despite being single-threaded.
Never hardcode configuration values in your code. Use environment variables instead:
// config.js
module.exports = {
port: process.env.PORT || 3000,
dbUrl: process.env.DATABASE_URL || 'mongodb://localhost:27017/myapp',
nodeEnv: process.env.NODE_ENV || 'development'
};
Code language: JavaScript (javascript)
For local development, use a .env
file with the dotenv
package:
// At the very top of your entry file
require('dotenv').config();
// Now process.env contains variables from .env file
Code language: JavaScript (javascript)
Make sure to add .env
to your .gitignore
to avoid committing sensitive data.
The built-in debugger in NodeJS is incredibly powerful:
# Start your app in debug mode
$ node --inspect app.js
# Break immediately on start
$ node --inspect-brk app.js
Code language: PHP (php)
Then open Chrome and navigate to chrome://inspect
to connect to the debugger.
For simple debugging, use console.log
with descriptive labels:
console.log('[USER_SERVICE]', 'Fetching user with ID:', userId);
Code language: JavaScript (javascript)
Even better, use the debug
package for togglable logs:
const debug = require('debug')('app:userService');
function getUser(id) {
debug(`Fetching user with ID: ${id}`);
// ...
}
// Run your app with DEBUG=app:* to see these logs
// $ DEBUG=app:* node app.js
Code language: JavaScript (javascript)
Security should never be an afterthought. Here are some essential practices:
// Use rate limiting for APIs
const rateLimit = require('express-rate-limit');
app.use(rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each IP to 100 requests per windowMs
}));
// Set security headers
const helmet = require('helmet');
app.use(helmet());
// Validate user input
const { body, validationResult } = require('express-validator');
app.post('/user',
body('email').isEmail(),
body('password').isLength({ min: 8 }),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Process valid input
}
);
Code language: PHP (php)
Never trust user input, and always sanitize it before using it in queries or commands.
Performance matters, especially as your application grows:
// Use streams for handling large files
const fs = require('fs');
fs.createReadStream('bigFile.txt')
.pipe(transformStream)
.pipe(fs.createWriteStream('output.txt'));
// Implement caching for expensive operations
const memoize = require('lodash/memoize');
const expensiveOperation = memoize((input) => {
// Complex calculation
return result;
});
// Use worker threads for CPU-intensive tasks
const { Worker } = require('worker_threads');
function runWorker(data) {
return new Promise((resolve, reject) => {
const worker = new Worker('./worker.js');
worker.postMessage(data);
worker.on('message', resolve);
worker.on('error', reject);
});
}
Code language: JavaScript (javascript)
Never run NodeJS apps directly with node
in production. Use a process manager:
# Install PM2 globally
$ npm install -g pm2
# Start your application
$ pm2 start app.js --name "my-api" --watch
# Run in cluster mode to utilize all CPU cores
$ pm2 start app.js -i max
# View logs
$ pm2 logs
# Monitor performance
$ pm2 monit
Code language: PHP (php)
PM2 ensures your app restarts after crashes and can scale across multiple CPU cores. (learn more about nodejs clustering)
Make sure your application shuts down gracefully:
const server = app.listen(3000);
function gracefulShutdown() {
console.log('Shutting down gracefully...');
server.close(() => {
console.log('HTTP server closed');
// Close database connections
mongoose.connection.close(false, () => {
console.log('Database connection closed');
process.exit(0);
});
});
// If it takes too long, force exit
setTimeout(() => {
console.error('Could not close connections in time, forcing shutdown');
process.exit(1);
}, 10000);
}
// Listen for shutdown signals
process.on('SIGTERM', gracefulShutdown);
process.on('SIGINT', gracefulShutdown);
Code language: JavaScript (javascript)
This ensures all connections are properly closed before the application exits.
NodeJS now supports most modern JavaScript features. Use them to write cleaner code:
// Destructuring
const { name, age } = user;
// Spread operator
const updatedUser = { ...user, lastLogin: new Date() };
// Optional chaining
const city = user?.address?.city;
// Nullish coalescing
const displayName = user.nickname ?? user.name;
// Async/await with for...of
async function processItems(items) {
for (const item of items) {
await processItem(item);
}
}
Code language: JavaScript (javascript)
NodeJS continues to evolve rapidly, and staying up-to-date with best practices is crucial. The tips in this guide will help you avoid common pitfalls and write more robust code, but they’re just the beginning of your NodeJS journey.
Remember that software development rarely has a “one size fits all” solution. Always evaluate your project’s specific needs before applying any pattern or technique.
What NodeJS tips have you discovered in your own experience? Share them in the comments, and I’ll consider adding them to this guide. Together, we can build a comprehensive resource for the NodeJS community.
Happy coding! 🚀
Unlock the full potential of service workers with advanced features like push notifications, background sync, and performance optimization techniques that transform your web app into…
Learn how to integrate service workers in React, Next.js, Vue, and Angular with practical code examples and production-ready implementations for modern web applications.
Master the essential service worker caching strategies that transform web performance. Learn Cache-First, Network-First, and Stale-While-Revalidate patterns with practical examples that'll make your apps blazingly…
This website uses cookies.
View Comments
This is a nice work, (Y)
exit node shell: CTRL+C, twice