~/
Node.js Best Practices
Quiz
...

Node.js Best Practices

advanced · updated Tue Sep 22 2026Contribute

Project structure, security, and patterns for production Node apps.

Node.js Best Practices

You know the pieces. This lesson ties them together into habits that keep Node apps fast, safe, and maintainable.

Structure by Feature

Group files by what they do, not what they are:

src/
  users/
    users.routes.js
    users.service.js
    users.repo.js
  orders/
    orders.routes.js
    orders.service.js
  app.js
  server.js

Each feature owns its routes, logic, and data access. Easy to find, easy to delete.

Separate Server from App

Keep the Express app exportable and the listening call separate — this makes testing possible:

// app.js
export const app = express();
app.get("/health", (req, res) => res.json({ ok: true }));

// server.js
import { app } from "./app.js";
app.listen(process.env.PORT ?? 3000);

Tests can import app without opening a port.

Handle Errors Centrally

Log once, respond once:

process.on("unhandledRejection", (err) => {
  console.error("Unhandled rejection:", err);
  process.exit(1);
});

In Express, route all errors through one handler and give each error a status code.

Security Essentials

  • Validate and sanitize input — Never trust the client.
  • Use helmet — Sets safe HTTP headers.
  • Enable CORS deliberately — Allow only known origins.
  • Rate-limit — Protect auth and write endpoints.
  • Hash passwords — Use bcrypt or argon2, never plain text.
  • Keep dependencies updated — Run npm audit regularly.

Logging

Structured logs beat console.log in production:

console.log(JSON.stringify({ level: "info", msg: "user created", id: user.id }));

Tools like pino add levels, timestamps, and performance.

Graceful Shutdown

Close connections before exiting so in-flight requests finish:

process.on("SIGTERM", () => {
  server.close(() => {
    console.log("Shutting down");
    process.exit(0);
  });
});

Performance

  • Do not block the event loop — Offload CPU work to workers.
  • Cache expensive results — In memory or Redis.
  • Use streams — For large files and responses.
  • Connection pooling — Reuse database connections.

Best Practices

  1. One responsibility per module.
  2. Validate all input.
  3. Log with structure and levels.
  4. Shut down gracefully.
  5. Automate tests and security checks in CI.

Common Mistakes

  1. Doing everything in server.js.
  2. Trusting req.body or req.query.
  3. Blocking the event loop with sync code.
  4. Leaving secrets in the repo.
  5. Skipping error handling until production breaks.