Labs ICT
Pro Login

Node.js Best Practices

Patterns and practices for production code.

Always Use Async/Await

Callbacks lead to deeply nested code that is hard to read and debug. async/await makes asynchronous code look and behave like synchronous code.

// Don't: Callback hell
fs.readFile("a.txt", (err, a) => {
  fs.readFile("b.txt", (err, b) => {
    // Nested callbacks...
  });
});

// Do: Async/await
async function readFiles() {
  const a = await fs.readFile("a.txt", "utf-8");
  const b = await fs.readFile("b.txt", "utf-8");
}

Handle All Errors

Always wrap async code in try/catch. In Express, catch errors and pass them to next() so your error-handling middleware can respond properly.

app.get("/api/users/:id", async (req, res, next) => {
  try {
    const user = await User.findById(req.params.id);
    if (!user) return res.status(404).json({ error: "Not found" });
    res.json(user);
  } catch (err) {
    next(err);
  }
});

Use Environment Variables

Never hardcode secrets like database passwords or API keys. Use environment variables and the dotenv package to load them from a .env file.

// Don't hardcode secrets
const dbPassword = process.env.DB_PASSWORD;

// Use .env file (never commit this)
require("dotenv").config();

Structure Your Project

A clear folder structure keeps your code organized as the project grows. Separate concerns into routes, models, controllers, middleware, and utilities.

project/
  src/
    routes/      # API routes
    models/      # Database models
    controllers/ # Business logic
    middleware/   # Custom middleware
    utils/       # Helper functions
  tests/         # Test files
  .env           # Environment variables
  .gitignore     # Ignored files
  package.json   # Project config

Never Block the Event Loop

Synchronous operations and heavy CPU work block the event loop, making your server unresponsive. Use async APIs for I/O and worker_threads for CPU-intensive tasks.

// Don't: Synchronous file operations
const data = fs.readFileSync("huge-file.txt");

// Do: Async file operations
const data = await fs.readFile("huge-file.txt");

// Don't: CPU-intensive work on main thread
function fibonacci(n) {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
}

// Do: Use worker_threads for heavy computation

Use ESLint

ESLint catches bugs before they happen and enforces consistent code style across your project.

npm install --save-dev eslint
npx eslint --init

The Golden Rule: Write code that is easy to read, handles errors gracefully, and does not block the event loop. Everything else is secondary.

Back to Node.js Track →