Cheatsheet Node.js
Runtime JavaScript server-side
Node.js
Modules and Setup
CommonJS (require)
// math.js
function add(a, b) { return a + b; }
module.exports = { add };
// app.js
const { add } = require("./math");
console.log(add(2, 3)); // 5CommonJS is Node's classic module system. Use module.exports to export and require() to import.
Native modules
const fs = require("fs");
const path = require("path");
const http = require("http");
const the = require("the");
const crypto = require("crypto");
console.log(os.platform()); // "win32"
console.log(os.cpus().length); // 8Node includes native modules (fs, path, http, the, crypto...) that need no installation. Import them directly by name.
Environment variables
// .env
PORT=3000
DB_URL=mongodb://localhost/app
// app.js
require("dotenv").config();
const port = process.env.PORT || 3000;
// Node 20+ (without dotenv):
// node --env-file=.env app.jsEnvironment variables keep configuration out of the code. dotenv loads the .env file into process.env; on Node 20+ use --env-file.
ES Modules (import)
// package.json
{ "type": "module" }
// math.js
export function add(a, b) {
return a + b;
}
// app.js
import { add } from "./math.js";ES Modules are the modern standard. Enable them with "type": "module" in package.json. The .js extension is required in the import.
Dynamic import
// Loads only when needed
const module = await import("./pesado.js");
// Conditional
if (process.env.DEBUG) {
const { log } = await import("./debug.js");
log("modo debug");
}The dynamic import() loads modules on demand and returns a Promise. Useful for code-splitting and optional dependencies.
CLI arguments
// node app.js --name=Anna --age=30
const args = process.argv.slice(2);
// Simple parse:
const params = Object.fromEntries(
args.map(a => a.replace("--", "").split("="))
);
console.log(params.name); // "Anna"process.argv contains the command-line arguments. The first two are node and the file — use slice(2) to ignore them.
Exports: several ways
// Several functions:
module.exports = { add, subtract, PI };
// A class:
module.exports = class Server { ... };
// Add to the existing exports:
exports.name = "app";
exports.version = "1.0";module.exports is the object returned by require. exports is a shortcut — but reassigning exports = {} does not work (use module.exports).
__dirname and __filename
// CommonJS
console.log(__dirname); // the file's folder
console.log(__filename); // full path
const path = require("path");
const config = path.join(__dirname, "config.json");In CommonJS, __dirname is the current file's folder and __filename the full path. Essential for building absolute paths.
Events (EventEmitter)
const EventEmitter = require("events");
const emitter = new EventEmitter();
emitter.on("data", (d) => {
console.log("Received:", d);
});
emitter.once("start", () => console.log("1x"));
emitter.emit("data", { id: 1 });The EventEmitter is the base of Node's event pattern. on registers a listener, emit fires it and once runs only once.
Default vs Named
// math.js (ESM)
export default function add(a, b) {
return a + b;
}
export const PI = 3.14;
// app.js
import add, { PI } from "./math.js";A module can have one export default (imported without braces) and several named exports (with braces). Combine both in the same import.
import.meta (ESM)
// ES Modules (no __dirname)
import { fileURLToPath } from "url";
import path from "path";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);In ES Modules there is no __dirname. Use import.meta.url with fileURLToPath to get the file's path.
File System
Read a file
const fs = require("fs/promises");
// Asynchronous (recommended)
const content = await fs.readFile(
"data.txt", "utf-8"
);
// Synchronous (blocks the event loop)
const fsSync = require("fs");
const txt = fsSync.readFileSync("data.txt", "utf-8");Use fs/promises with await to avoid blocking. readFileSync is only acceptable in startup or CLI scripts.
List and check
const fs = require("fs/promises");
// List a directory
const items = await fs.readdir("./src");
// With the file type:
const comTipo = await fs.readdir("./src", {
withFileTypes: true,
});
comTipo.filter(e => e.isFile());
// Check existence
await fs.access("file.txt"); // throws an error if it doesn't existreaddir lists the contents of a folder. With withFileTypes you get the type of each entry. access checks existence (throws an error otherwise).
Delete and rename
const fs = require("fs/promises");
// Delete a file
await fs.unlink("temp.txt");
// Delete a folder (with contents)
await fs.rm("folder", { recursive: true });
// Rename / move
await fs.rename("old.txt", "new.txt");unlink deletes files. rm with recursive deletes whole folders. rename also works for moving.
Write a file
const fs = require("fs/promises");
// Overwrite (creates if it doesn't exist)
await fs.writeFile("log.txt", "Hello");
// Append at the end
await fs.appendFile("log.txt", "\nNova line");
// Create a folder (with subfolders)
await fs.mkdir("folder/sub", { recursive: true });writeFile overwrites the content and appendFile adds to the end. mkdir with recursive creates the whole hierarchy.
path module
const path = require("path");
path.join(__dirname, "src", "app.js")
path.basename("/a/b/f.txt") // "f.txt"
path.extname("photo.png") // ".png"
path.resolve("./config") // absolute path
path.dirname("/a/b/f.txt") // "/a/b"path handles paths safely on any OS. join concatenates with the correct separator (/ or \).
Information (stat)
const fs = require("fs/promises");
const info = await fs.stat("file.txt");
info.size // size in bytes
info.isFile() // true
info.isDirectory() // false
info.mtime // last modification
info.birthtime // creationstat returns file metadata: size, type and dates. Useful to check before processing or to display to the user.
Copy files
const fs = require("fs/promises");
// Single file
await fs.copyFile("source.txt", "destination.txt");
// Whole folder (Node 16.7+)
await fs.cp("folder-src", "folder-dst", {
recursive: true,
});copyFile copies a file. cp with recursive copies whole folders — useful for backups and templates.
Streams (large files)
const fs = require("fs");
const reading = fs.createReadStream("big.csv");
const writing = fs.createWriteStream("copy.csv");
reading.pipe(writing);
reading.on("data", (chunk) => {
console.log("Lido:", chunk.length, "bytes");
});
reading.on("end", () => console.log("End"));streams process files in blocks (chunks) without loading everything into memory. pipe connects the read to the write automatically.
Watch changes (watch)
const fs = require("fs");
const watcher = fs.watch("./src", (event, file) => {
console.log(event, file); // "change" "app.js"
});
// Stop watching
watcher.close();fs.watch notifies when files change (create, modify, delete). Useful for build tools and live reload. Call close() to stop.
Read and write JSON
const fs = require("fs/promises");
// Read
const text = await fs.readFile("config.json", "utf-8");
const config = JSON.parse(text);
// Write (formatted)
await fs.writeFile(
"config.json",
JSON.stringify(config, null, 2)
);Combine readFile/writeFile with JSON.parse and JSON.stringify. The third argument (2) formats with indentation.
Stream pipeline
const { pipeline } = require("stream/promises");
const fs = require("fs");
const zlib = require("zlib");
await pipeline(
fs.createReadStream("data.csv"),
zlib.createGzip(),
fs.createWriteStream("data.csv.gz")
);pipeline chains streams and propagates errors correctly (unlike the simple pipe). Ideal for transforming and compressing data.
Asynchronism
Promises
function wait(ms) {
return new Promise((resolve) =>
setTimeout(resolve, ms)
);
}
wait(1000).then(() => {
console.log("1s after");
});
// Reject:
new Promise((_, reject) =>
reject(new Error("failed"))
);A Promise represents a future value. resolve indicates success and reject indicates an error. It is consumed with .then().
setTimeout / setInterval
// Once after 2s
setTimeout(() => {
console.log("delayed");
}, 2000);
// Repeat every 1s
const id = setInterval(() => {
console.log("tick");
}, 1000);
clearInterval(id); // stop
clearTimeout(id); // cancel timeoutsetTimeout runs once after a delay and setInterval repeats periodically. Store the ID to cancel with clear.
AbortController
const controller = new AbortController();
// Cancel after 3s
const timeout = setTimeout(
() => controller.abort(), 3000
);
try {
const resp = await fetch(url, {
signal: controller.signal,
});
} catch (e) {
console.log("Cancelado:", e.name); // "AbortError"
}AbortController cancels asynchronous operations (like fetch). Pass the signal and call abort() to interrupt.
async / await
async function fetchData() {
try {
const resp = await fetch(url);
const data = await resp.json();
return data;
} catch (error) {
console.error("Failure:", error.message);
throw error;
}
}async/await makes asynchronous code readable like synchronous code. await pauses until the Promise resolves. try/catch handles errors.
setImmediate / nextTick
console.log("1 - synchronous");
process.nextTick(() => console.log("2 - nextTick"));
Promise.resolve().then(() => console.log("3 - microtask"));
setImmediate(() => console.log("4 - immediate"));
setTimeout(() => console.log("5 - timer"), 0);
// Typical order: 1, 2, 3, 4/5process.nextTick runs before microtasks. setImmediate executes in the check phase of the event loop. Both defer code without using timers.
Limited concurrency
async function mapLimit(items, limit, fn) {
const results = [];
for (let i = 0; i < items.length; i += limit) {
const lote = items.slice(i, i + limit);
results.push(...await Promise.all(lote.map(fn)));
}
return results;
}
await mapLimit(urls, 5, (u) => fetch(u));To avoid opening hundreds of requests in parallel, process in batches (Promise.all per group). It controls memory and socket usage.
Promise.all / allSettled
// All in parallel (fails if one fails)
const [a, b, c] = await Promise.all([
fetch("/api/a"),
fetch("/api/b"),
fetch("/api/c"),
]);
// All (even with errors)
const results = await Promise.allSettled(promessas);
results.filter(r => r.status === "fulfilled");Promise.all runs in parallel but rejects if one fails. allSettled waits for all and returns the state of each one.
Promisify (callbacks)
const { promisify } = require("util");
const fs = require("fs");
// Convert callback → Promise
const read = promisify(fs.readFile);
const content = await read("data.txt", "utf-8");
// Today fs/promises is preferred:
const fsp = require("fs/promises");util.promisify converts callback functions ((err, result) style) into Promises. Nowadays, prefer the native */promises modules.
Handle errors (pattern)
// Wrapper to avoid repeated try/catch
async function capture(promise) {
try {
const data = await promise;
return [data, null];
} catch (error) {
return [null, error];
}
}
const [data, error] = await capture(fetch(url));
if (error) console.error(error);This [data, error] pattern (inspired by Go) avoids nested try/catch. Check for an error before using the data.
Promise.race / any
// First to resolve OR reject
const fastest = await Promise.race([
fetch("/api/slow"),
wait(5000).then(() => "timeout"),
]);
// First to resolve (ignores errors)
const first = await Promise.any([
fetch("/api/server1"),
fetch("/api/server2"),
]);race returns the first result (success or error). any returns the first success, ignoring rejections. Useful for timeouts.
Async iterators (for await)
const fs = require("fs");
// Read a stream chunk by chunk
const stream = fs.createReadStream("big.txt");
for await (const chunk of stream) {
console.log("Block:", chunk.length);
}for await...of iterates over asynchronous sources (streams, async generators) sequentially. It consumes each chunk the it arrives.
Event Loop (concept)
console.log("1 - synchronous");
setTimeout(() => console.log("3 - timer"), 0);
Promise.resolve().then(
() => console.log("2 - microtask")
);
// Order: 1, 2, 3
// Synchronous → Microtasks → TimersThe Event Loop processes: synchronous code → microtasks (Promises) → timers (setTimeout). Even with delay 0, the timer runs last.
HTTP and Server
Native HTTP server
const http = require("http");
const server = http.createServer((req, res) => {
res.writeHead(200, {
"Content-Type": "text/plain",
});
res.end("Hello do Node!");
});
server.listen(3000, () => {
console.log("Em http://localhost:3000");
});The http module creates a server with no dependencies. The callback receives the request (req) and the response (res). listen starts it on the port.
Serve static files
const http = require("http");
const fs = require("fs");
const path = require("path");
http.createServer((req, res) => {
const file = path.join("./public", req.url);
fs.readFile(file, (error, data) => {
if (error) {
res.writeHead(404);
return res.end("Not found");
}
res.end(data);
});
}).listen(3000);A static file server reads from disk and returns it to the client. In production, use Express (express.static) or a CDN.
Redirects
// 301 (permanent)
res.writeHead(301, { Location: "/new-page" });
res.end();
// 302 (temporary)
res.writeHead(302, { Location: "/login" });
res.end();301 is permanent (SEO transfers to the new URL). 302 is temporary. The browser follows the Location header automatically.
fetch (Node 18+)
// GET
const resp = await fetch("https://api.example.com/data");
const data = await resp.json();
// POST
await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "Anna" }),
});Since Node 18, fetch is native (no installation needed). It works like in the browser: resp.json() to read the body.
URL and query params
const url = new URL(
"http://site.com/search?q=node&p=2"
);
url.pathname // "/search"
url.searchParams.get("q") // "node"
url.searchParams.get("p") // "2"
// Build a URL:
const u = new URL("http://api.com/data");
u.searchParams.set("page", "1");The URL class parses and builds URLs. searchParams gives easy access to the query string parameters.
HTTPS / TLS
const https = require("https");
const fs = require("fs");
const options = {
key: fs.readFileSync("key.pem"),
cert: fs.readFileSync("cert.pem"),
};
https.createServer(options, (req, res) => {
res.end("secure connection");
}).listen(443);The https module creates an encrypted server. It needs a key (key) and certificate (cert). In production, a reverse proxy is normally used.
fetch with timeout
const resp = await fetch(url, {
signal: AbortSignal.timeout(5000), // 5s
});
if (!resp.ok) {
throw new Error("HTTP " + resp.status);
}
const data = await resp.json();AbortSignal.timeout() cancels the fetch after a limit. Always check resp.ok — fetch does not throw an error on 4xx/5xx status.
Headers and status
// Read request headers:
req.headers["content-type"]
req.headers["authorization"]
// Set on the response:
res.writeHead(200, {
"Content-Type": "application/json",
"Cache-Control": "no-cache",
"X-Custom": "value",
});headers carry metadata (content type, authentication, cache). Read them in req.headers and set them with writeHead.
Read request body (POST)
http.createServer((req, res) => {
let body = "";
req.on("data", (chunk) => { body += chunk; });
req.on("end", () => {
const data = JSON.parse(body);
res.end("Received: " + data.name);
});
}).listen(3000);On the native server, the body arrives in chunks via data events. Accumulate them and process on the end event. Express simplifies this with express.json().
Cookies
// Set a cookie on the response:
res.writeHead(200, {
"Set-Cookie": "session=abc123; HttpOnly; Path=/",
});
// Read cookies from the request:
const cookies = req.headers.cookie; // "session=abc123"cookies are set with the Set-Cookie header and read in req.headers.cookie. The HttpOnly flag prevents access via JavaScript.
Express.js
Express setup
npm install express
const express = require("express");
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.listen(3000, () => {
console.log("Server active na port 3000");
});Express is Node's most popular web framework. express.json() parses the JSON body automatically.
Middleware
// Custom logger
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next(); // pass to the next
});
// Error middleware (4 arguments)
app.use((error, req, res, next) => {
console.error(error.stack);
res.status(500).json({ error: error.message });
});middleware runs between the request and the route. next() passes to the next one. The error handler has 4 arguments and must be last.
Validation with middleware
function validarId(req, res, next) {
const id = Number(req.params.id);
if (isNaN(id)) {
return res.status(400).json({
error: "ID must be numeric",
});
}
req.id = id;
next();
}
app.get("/users/:id", validarId, (req, res) => {
res.json({ id: req.id });
});A validation middleware checks the data before the route. If it fails, it responds with an error and does not call next().
Routes and parameters
app.get("/", (req, res) => {
res.send("Start");
});
// Route parameter
app.get("/users/:id", (req, res) => {
res.json({ id: req.params.id });
});
// Query string ?search=x
app.get("/search", (req, res) => {
res.json({ q: req.query.search });
});Routes map method + path. The :params capture URL segments. req.query reads the query string.
Chain handlers
function validate(req, res, next) {
if (!req.body.name) return next(new Error("name"));
next();
}
function authenticate(req, res, next) {
if (!req.user) return res.status(401).end();
next();
}
app.post("/data", authenticate, validate, (req, res) => {
res.json({ ok: true });
});You can pass several handlers on a route — they run in sequence. Each one calls next() to advance or responds/throws an error to stop.
File upload (multer)
npm install multer
const multer = require("multer");
const upload = multer({ dest: "uploads/" });
app.post("/upload", upload.single("file"),
(req, res) => {
console.log(req.file.originalname);
console.log(req.file.path);
res.json({ ok: true });
}
);multer handles multipart uploads. single() accepts one file. The data ends up in req.file (name, path, size).
req object (request)
app.post("/users/:id", (req, res) => {
req.params.id // route parameter
req.query.active // query string
req.body // body (with express.json)
req.headers // headers
req.method // "POST"
req.path // "/users/5"
req.ip // client IP
});The req object carries everything about the request: params, query, body, headers, method and ip.
Responses (res)
res.send("text HTML");
res.json({ ok: true });
res.status(201).json({ id: 1 });
res.redirect("/login");
res.download("/file.pdf");
res.sendFile(path.join(__dirname, "index.html"));
res.status(204).end(); // no bodyThe res object has methods for each response type. json() sets the Content-Type automatically. 204 is "success with no content".
app.set and configuration
app.set("port", process.env.PORT || 3000);
app.set("view engine", "ejs");
// Read configuration
const port = app.get("port");
// Enable/disable options
app.enable("trust proxy");
app.disable("x-powered-by");
app.listen(app.get("port"));app.set stores configuration key-value pairs and app.get("key") reads them. enable/disable toggle boolean flags.
Modular router
// routes/products.js
const router = express.Router();
router.get("/", list);
router.post("/", create);
router.get("/:id", ver);
router.put("/:id", update);
router.delete("/:id", delete);
module.exports = router;
// app.js
app.use("/products", require("./routes/products"));The Router organizes routes into separate files. Each module handles a resource. The prefix is set in app.use.
Static files
// Serve the public/ folder
app.use(express.static("public"));
// With a prefix
app.use("/assets", express.static("public/assets"));
// Access: http://localhost:3000/img/logo.png
// Or: http://localhost:3000/assets/style.cssexpress.static serves files directly (CSS, JS, images). Without a prefix, the file is accessed from the root; with a prefix, it goes under that path.
404 and error handler
// 404: no route matched (at the end)
app.use((req, res) => {
res.status(404).json({ error: "Not found" });
});
// Error handler (4 args, after the 404)
app.use((error, req, res, next) => {
res.status(error.status || 500).json({
error: error.message,
});
});A final middleware with no path catches nonexistent routes (404). The error handler (4 args) centralizes failures. Both must be at the end.
API REST e Auth
Complete CRUD
let products = [];
app.get("/products", (req, res) =>
res.json(products));
app.post("/products", (req, res) => {
const new = { id: Date.now(), ...req.body };
products.push(new);
res.status(201).json(new);
});
app.delete("/products/:id", (req, res) => {
products = products.filter(p => p.id != req.params.id);
res.status(204).end();
});A CRUD uses GET (list), POST (create, 201), PUT (update) and DELETE (204). The data comes in req.body.
Error handling
class ApiError extends Error {
constructor(status, message) {
super(message);
this.status = status;
}
}
// In the route:
throw new ApiError(404, "Product not found");
// Global handler (last middleware):
app.use((error, req, res, next) => {
const status = error.status || 500;
res.status(status).json({ error: error.message });
});Create an ApiError class with the HTTP status. The global handler centralizes all error responses in one place.
Password hashing (bcrypt)
npm install bcrypt
const bcrypt = require("bcrypt");
// Signup: store the hash (never the plain password)
const hash = await bcrypt.hash(password, 10);
// Login: compare
const ok = await bcrypt.compare(password, hash);
if (!ok) return res.status(401).end();Never store passwords in plain text. bcrypt.hash generates a hash with salt (cost 10) and compare verifies it at login.
HTTP status codes
res.status(200) // OK res.status(201) // Created res.status(204) // Success, without content res.status(400) // Order invalid res.status(401) // Not autenticado res.status(403) // Sem permission res.status(404) // Not found res.status(500) // Error do server
Status codes communicate the result: 2xx success, 4xx client error, 5xx server error. Use them correctly instead of always returning 200.
CORS
npm install cors
const cors = require("cors");
// Allow everything (development)
app.use(cors());
// Specific configuration (production)
app.use(cors({
origin: "http://localhost:5173",
methods: ["GET", "POST", "PUT", "DELETE"],
credentials: true,
}));CORS controls which domains can access the API. In production, specify the exact origin instead of allowing everything.
Pagination
app.get("/products", (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const start = (page - 1) * limit;
const results = products.slice(start, start + limit);
res.json({
data: results,
total: products.length,
page,
pages: Math.ceil(products.length / limit),
});
});Pagination returns only a slice of the data. The client sends page and limit. The response includes the total for the frontend.
Input validation
function validarProduto(req, res, next) {
const { name, price } = req.body;
if (!name || name.length < 3) {
return res.status(400).json({
error: "Name must have 3+ characters",
});
}
if (typeof price !== "number" || price < 0) {
return res.status(400).json({ error: "Price invalid" });
}
next();
}
app.post("/products", validarProduto, create);Always validate client data. Check type, length and format. Respond with 400 and a clear message if something fails.
Rate limiting
npm install express-rate-limit
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per IP
message: { error: "Many orders" },
});
app.use("/api/", limiter);rate limiting protects against abuse and DDoS. It limits the number of requests per IP within a time window (windowMs).
API versioning
// By URL prefix (most common)
app.use("/api/v1/products", routerV1);
app.use("/api/v2/products", routerV2);
// Or by Router
const v1 = express.Router();
v1.get("/products", listarV1);
app.use("/api/v1", v1);Versioning the API (e.g. /api/v1) lets it evolve without breaking old clients. Each version has its own router.
Validate with Zod
npm install zod
const { z } = require("zod");
const schema = z.object({
name: z.string().min(3),
price: z.number().positive(),
});
app.post("/products", (req, res) => {
const result = schema.safeParse(req.body);
if (!result.success) {
return res.status(400).json(result.error.issues);
}
// result.data is validated and typed
});Zod validates and transforms data with declarative schemas. safeParse does not throw — it returns success and the issues.
JWT authentication
npm install jsonwebtoken
const jwt = require("jsonwebtoken");
// Generate token:
const token = jwt.sign(
{ id: user.id }, "secret", { expiresIn: "1h" }
);
// Verify (middleware):
function auth(req, res, next) {
const token = req.headers.authorization?.split(" ")[1];
try {
req.user = jwt.verify(token, "secret");
next();
} catch {
res.status(401).json({ error: "Not autorizado" });
}
}JWT authenticates without a session. The token goes in the Authorization: Bearer ... header. The middleware verifies it and injects the user into req.user.
Request logs (morgan)
npm install morgan
const morgan = require("morgan");
// Predefined format
app.use(morgan("dev")); // colorido to dev
app.use(morgan("combined")); // formato Apache
// Custom output
app.use(morgan(":method :url :status :response-time ms"));morgan logs each HTTP request (method, URL, status, time). The dev format is colorful; combined is for production.
NPM and Packages
Start a project
npm init -y // create package.json npm install // install dependencies npm install express // dependency de production npm install -D nodemon // dependency de dev npm install -g pm2 // global (CLI)
npm init -y creates package.json with default values. -D saves to devDependencies (development only).
package-lock.json
// The lock records the EXACT versions of everything
// (including transitive dependencies)
npm ci // clean install based on the lock
// (faster, reproducible)
// Rules:
// - Never edit manually
// - Always commit to git
// - Use npm ci in CI/CDpackage-lock.json ensures everyone installs exactly the same versions. npm ci is ideal for servers and CI.
engines and module type
// package.json
{
"type": "module", // usar ES Modules
"engines": {
"node": ">=20.0.0" // version minimum
}
}
// "type": "commonjs" is o default (require)"type": "module" enables ES Modules in .js files. engines documents the minimum required Node version.
Remove and list packages
npm uninstall package // remove npm uninstall -D nodemon // remove de dev npm ls // tree de dependencies npm ls --depth=0 // only diretas npm outdated // versions desatualizadas npm audit // vulnerabilities
uninstall removes a package and updates package.json. npm ls shows the tree and audit detects known vulnerabilities.
npx
// Run without installing globally npx create-react-app my-app npx nodemon app.js npx jest --watch // Specific version npx node@18 -v // Local packages from node_modules/.bin npx eslint src/
npx runs packages without installing them globally. If the package exists in node_modules, it uses it; otherwise, it downloads it temporarily.
.npmrc and registries
# .npmrc (project or ~/.npmrc)
registry=https://registry.npmjs.org/
@my-company:registry=https://npm.company.com/
//npm.company.com/:_authToken=${NPM_TOKEN}
# Save token with login:
npm login --registry=https://npm.company.com/.npmrc configures the registry and authentication tokens. It allows using private registries for scoped packages (@company).
package.json scripts
{
"scripts": {
"start": "node app.js",
"dev": "nodemon app.js",
"test": "jest",
"lint": "eslint src/"
}
}
// Run:
npm run dev
npm start // "start" not precisa de "run"
npm testscripts automate commands. npm run name runs any script. start and test work without run.
Workspaces (monorepo)
// package.json (root)
{
"workspaces": ["packages/*"]
}
// Structure:
// packages/api/package.json
// packages/frontend/package.json
// packages/shared/package.json
npm install // installs everything
npm run dev -w api // runs in a workspaceworkspaces manage multiple packages in a single repository. Shared dependencies are installed once at the root.
Publish a package
// Minimal package.json:
{
"name": "my-package",
"version": "1.0.0",
"main": "index.js"
}
npm login
npm publish
// Update version:
npm version patch // 1.0.0 → 1.0.1
npm version minor // 1.0.1 → 1.1.0
npm version major // 1.1.0 → 2.0.0To publish, you need an account on npmjs.com. npm version updates the version and creates a commit/tag automatically.
Version management (semver)
npm install package // latest version npm install package@1.2.3 // version exata npm install package@^1.2.0 // compatible 1.x.x npm install package@~1.2.0 // only patches 1.2.x npm outdated // ver desatualizados npm update // update inside do range
In semver: ^ accepts minor+patch, ~ only patch. outdated shows what is old and update respects the ranges.
bin and package CLI
// package.json
{
"name": "my-cli",
"bin": { "my-cli": "./cli.js" }
}
// cli.js (first line)
#!/usr/bin/env node
console.log("Hello da CLI!");
// npm link → makes the command availableThe bin field registers the package's executable commands. The shebang #!/usr/bin/env node indicates the interpreter. npm link installs it locally for testing.
CLI, Tests and Best Practices
Watch mode (Node 18+)
// Restart on file save node --watch app.js // With environment variables node --watch --env-file=.env app.js // Check versions node -v npm -v
--watch restarts the server automatically when you save the file. It replaces nodemon in simple projects.
Recommended structure
project/
src/
routes/ // routes by resource
controllers/ // handler logic
services/ // business logic
models/ // data access
middleware/ // validation, auth
app.js // Express configuration
package.json
.env
.gitignoreSeparate by responsibility: routes define URLs, controllers orchestrate, services contain the logic. Each file does one thing.
API tests (supertest)
npm install -D supertest vitest
const request = require("supertest");
it("creates product", async () => {
const resp = await request(app)
.post("/products")
.send({ name: "Keyboard", price: 50 });
expect(resp.status).toBe(201);
expect(resp.body.id).toBeDefined();
});supertest tests HTTP endpoints without starting the server. Pass the Express app and simulate requests with get/post and send.
Debugging
// Inspect with Chrome DevTools
node --inspect app.js
node --inspect-brk app.js // pause on start
// Useful logs
console.log("info");
console.warn("warning");
console.error("error");
console.table(arrayOfObjects);
console.time("op");
console.timeEnd("op"); // "op: 123ms"--inspect opens Chrome DevTools to debug. console.table shows arrays/objects in a table. time/timeEnd measures duration.
Best practices
// Use async/await (not callbacks) // Always handle errors (try/catch) // Config in environment variables // Small, focused modules // ESLint + Prettier for consistency // Never commit node_modules // Use .gitignore and .env.example
Conventions that keep Node code clean and production-ready. .env.example documents the required variables without exposing secrets.
Basic security (helmet)
npm install helmet
const helmet = require("helmet");
app.use(helmet()); // headers de security
// Never expose stack traces in production:
app.use((error, req, res, next) => {
res.status(500).json({
error: process.env.NODE_ENV === "production"
? "Error internal"
: error.message,
});
});helmet adds security headers automatically. In production, never expose stack traces — show only "Error internal".
Process and shutdown
// Shut down gracefully (Ctrl+C)
process.on("SIGINT", () => {
console.log("Shutting down...");
server.close(() => process.exit(0));
});
// Uncaught error
process.on("uncaughtException", (e) => {
console.error("Fatal:", e);
process.exit(1);
});
// Rejected promise without catch
process.on("unhandledRejection", (r) => {
console.error("Rejected:", r);
});Handle SIGINT to shut down without losing data. uncaughtException is the last resort — log the error and exit.
ESLint and Prettier
npm install -D eslint prettier // eslint.config.js (flat config) npx eslint --init // Format on save (VS Code) // "editor.formatOnSave": true // "editor.defaultFormatter": "esbenp.prettier-vscode" npx eslint src/ --fix
ESLint detects errors and style issues; Prettier formats code automatically. Use them together for consistency.
Performance and profiling
// Generate a CPU profile
node --prof app.js
node --prof-process isolate-*.log > profile.txt
// Heap snapshot (memory)
node --inspect app.js
// → DevTools → Memory → Take snapshot
// Monitor the event loop
const { monitorEventLoopDelay } = require("perf_hooks");--prof generates CPU profiles and --inspect allows heap snapshots in DevTools. Essential to find bottlenecks and memory leaks.
NODE_ENV and configuration
const env = process.env.NODE_ENV || "development";
const config = {
development: { debug: true, dbUrl: "localhost" },
production: { debug: false, dbUrl: process.env.DB_URL },
}[env];
// Start:
// NODE_ENV=production node app.jsNODE_ENV distinguishes environments (development/production). It adjusts behavior such the debug, logging and database connections.
Tests (Vitest / Jest)
npm install -D vitest
// math.test.js
import { describe, it, expect } from "vitest";
import { add } from "./math.js";
describe("add", () => {
it("sum two numbers", () => {
expect(add(2, 3)).toBe(5);
});
});
// npx vitestVitest (or Jest) tests functions in isolation. describe groups tests and expect checks results. Run it with npx vitest.
Advanced
Buffers (binary data)
const buf = Buffer.from("Hello", "utf-8");
buf.length // 4 bytes
buf.toString("utf-8") // "Hello"
buf.toString("base64") // "T2zDoA=="
// Allocate and write
const b = Buffer.alloc(8);
b.writeUInt32BE(1234, 0);
Buffer.concat([buf, b]);The Buffer represents raw binary data (outside the string). Essential for files, network and encryption. Convert with toString(encoding).
Crypto: Hash
const crypto = require("crypto");
const hash = crypto
.createHash("sha256")
.update("secret text")
.digest("hex");
// MD5 (do not use for security)
crypto.createHash("md5").update("x").digest("hex");crypto.createHash generates one-way hashes (sha256, md5). Use SHA-256 for integrity; for passwords prefer bcrypt.
Socket.IO
npm install socket.io
const io = require("socket.io")(servidorHttp);
io.on("connection", (socket) => {
socket.on("chat", (msg) => {
io.emit("chat", msg); // send a all
});
socket.join("sala1");
io.to("sala1").emit("new", "data");
});Socket.IO adds rooms, events and automatic fallback over WebSockets. emit sends to everyone; to(room) restricts to a group.
Child Processes
const { exec, spawn } = require("child_process");
// exec: output in a buffer (short commands)
exec("ls -la", (error, stdout) => {
console.log(stdout);
});
// spawn: stream (long / real-time output)
const proc = spawn("node", ["-v"]);
proc.stdout.on("data", (d) => console.log(d.toString()));The child_process module runs external commands. exec returns the full output; spawn streams it (better for long processes).
Crypto: HMAC and tokens
const crypto = require("crypto");
// HMAC (hash with a secret key)
const hmac = crypto
.createHmac("sha256", "secret")
.update("data")
.digest("hex");
// Secure random token
const token = crypto.randomBytes(32).toString("hex");createHmac generates an authenticated hash with a key (verifies integrity + origin). randomBytes creates cryptographically secure tokens.
the module (system)
const the = require("the");
os.platform() // "win32" | "linux" | "darwin"
os.arch() // "x64" | "arm64"
os.cpus().length // CPU colors
os.totalmem() // RAM total (bytes)
os.freemem() // RAM free
os.homedir() // user folder
os.tmpdir() // temporary folderThe the module gives operating system information: platform, architecture, CPU and memory. Useful to adapt behavior to the environment.
Cluster (multi-core)
const cluster = require("cluster");
const the = require("the");
if (cluster.isPrimary) {
const numCpus = os.cpus().length;
for (let i = 0; i < numCpus; i++) {
cluster.fork(); // one worker por CPU
}
} else {
require("./server"); // each worker corre o server
}cluster creates one process per CPU core, sharing the same port. It works around Node's single-thread limit to use all colors.
Crypto: AES encryption
const crypto = require("crypto");
const key = crypto.scryptSync("password", "salt", 32);
const iv = crypto.randomBytes(16);
const cifra = crypto.createCipheriv("aes-256-cbc", key, iv);
const encriptado = Buffer.concat([
cifra.update("message secreta", "utf-8"),
cifra.final(),
]);createCipheriv encrypts with AES (reversible). Derive the key with scryptSync and use a random iv. To decrypt use createDecipheriv.
Performance (perf_hooks)
const { performance } = require("perf_hooks");
const start = performance.now();
// ... operation ...
const duration = performance.now() - start;
console.log(`Took ${duration.toFixed(2)}ms`);
// Measure with markers
performance.mark("start");
performance.mark("end");
performance.measure("op", "start", "end");perf_hooks measures time with high precision (decimal milliseconds). performance.now() is more precise than Date.now().
Worker Threads
const { Worker, isMainThread, parentPort } =
require("worker_threads");
if (isMainThread) {
const w = new Worker(__filename);
w.postMessage("calculate");
w.on("message", (r) => console.log(r));
} else {
parentPort.on("message", () => {
parentPort.postMessage("done: " + 40 * 40);
});
}worker_threads run JavaScript in parallel (real threads), ideal for CPU-intensive tasks. They communicate via postMessage.
WebSockets (ws)
npm install ws
const { WebSocketServer } = require("ws");
const wss = new WebSocketServer({ port: 8080 });
wss.on("connection", (ws) => {
ws.on("message", (msg) => {
ws.send("eco: " + msg);
});
});WebSockets keep a bidirectional real-time connection. The ws package creates the server; each connection receives and sends messages.