Node.js — Files, HTTP, Express
একই JavaScript, কিন্তু এবার server-এ
1. Built-in Modules
// ESM imports — package.json: "type": "module"
import { readFile, writeFile } from "node:fs/promises";
import path from "node:path";
import os from "node:os";
console.log(os.platform(), os.cpus().length);
console.log(path.join(import.meta.dirname, "data.json"));
const text = await readFile("hello.txt", "utf8");
await writeFile("out.txt", text.toUpperCase());
fs/promises, path, os, process — এই কয়টি built-in module-ই দৈনিক কাজের ভিত্তি।2. Tiny HTTP Server
import { createServer } from "node:http";
const server = createServer((req, res) => {
if (req.url === "/" && req.method === "GET") {
res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
res.end("হ্যালো from Node!");
} else {
res.writeHead(404);
res.end("not found");
}
});
server.listen(3000, () => console.log("http://localhost:3000"));
3. Express — Less Boilerplate
$ npm install express
import express from "express";
const app = express();
app.use(express.json()); // parse JSON bodies
app.get("/", (req, res) => res.send("hi"));
app.get("/users/:id", (req, res) => res.json({ id: req.params.id }));
app.post("/users", (req, res) => {
const u = { id: Date.now(), ...req.body };
res.status(201).json(u);
});
app.listen(3000, () => console.log("up on 3000"));
4. A Tiny REST API — Todos
import express from "express";
const app = express();
app.use(express.json());
let todos = [{ id: 1, text: "buy chal", done: false }];
app.get("/todos", (req, res) => res.json(todos));
app.get("/todos/:id", (req, res) => {
const t = todos.find(x => x.id === +req.params.id);
if (!t) return res.status(404).json({ error: "not found" });
res.json(t);
});
app.post("/todos", (req, res) => {
const t = { id: Date.now(), text: req.body.text, done: false };
todos.push(t);
res.status(201).json(t);
});
app.patch("/todos/:id", (req, res) => {
const t = todos.find(x => x.id === +req.params.id);
if (!t) return res.sendStatus(404);
Object.assign(t, req.body);
res.json(t);
});
app.delete("/todos/:id", (req, res) => {
todos = todos.filter(x => x.id !== +req.params.id);
res.sendStatus(204);
});
app.listen(3000);
5. Sandbox-Safe API Simulation
The runner can't open ports, but the same data layer runs perfectly here.
let todos = [{ id: 1, text: "buy chal", done: false }];
const api = {
list() { return todos; },
get(id) { return todos.find(t => t.id === id) || null; },
create(text) {
const t = { id: Date.now(), text, done: false };
todos.push(t);
return t;
},
update(id, patch) {
const t = this.get(id);
if (!t) return null;
Object.assign(t, patch);
return t;
},
remove(id) {
const before = todos.length;
todos = todos.filter(t => t.id !== id);
return before !== todos.length;
}
};
const a = api.create("finish ABCL course");
api.update(a.id, { done: true });
console.log(api.list());
6. Middleware
// Logging
app.use((req, res, next) => {
console.log(req.method, req.url);
next();
});
// Auth gate
app.use("/api", (req, res, next) => {
if (!req.headers.authorization) return res.sendStatus(401);
next();
});
// Error handler — must take 4 args
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: "internal" });
});
7. Reading Environment Variables
// .env file (use `dotenv`)
DATABASE_URL=postgres://...
PORT=3000
// app.js
import "dotenv/config";
const port = process.env.PORT || 3000;
app.listen(port);
.env
Add it to .gitignore. Provide a .env.example with empty values for new contributors.
8. Glossary (শব্দকোষ)
| Term | Meaning | বাংলায় |
|---|---|---|
| Node.js | JavaScript runtime built on V8 + libuv for I/O. | V8 + libuv দিয়ে তৈরি JS runtime। |
| libuv | The C library that gives Node async file/network I/O. | Node-কে async I/O দেয় এই C library। |
fs/promises | Async filesystem API based on Promises. | Promise-based async fs API। |
http | Built-in HTTP server / client module. | Built-in HTTP server/client module। |
| Express | Most popular Node web framework — middleware + routes. | Node-এর জনপ্রিয় web framework। |
| Middleware | Function with (req, res, next) running in a chain. | (req, res, next) পদ্ধতির chain function। |
| REST | HTTP-based API style: GET/POST/PATCH/DELETE on resources. | Resource-এ GET/POST/PATCH/DELETE — REST style। |
process.env | Environment variables — config & secrets. | Environment variable — config ও secret। |
| Reverse proxy | Server (e.g. nginx) in front of Node for TLS, gzip, load balancing. | Node-এর সামনে nginx — TLS/gzip/load-balance। |
| Graceful shutdown | Stop accepting new connections; finish in-flight ones; exit. | নতুন connection বন্ধ → চলমান শেষ → exit। |
.env-এ — কখনোই commit করবেন না। Production-এ nginx-এর পেছনে রাখুন এবং SIGINT-এ graceful shutdown করুন।
9. Practice Problems
- Write a runnable in-memory CRUD store for "books".
✨ Show Answer
a1.jsconst books = []; const add = (t) => books.push({ id: books.length + 1, t }); add("পথের পাঁচালী"); add("আগুনের পরশমণি"); console.log(books); - Sketch a GET /books endpoint in Express.
✨ Show Answer
app.get("/books", (req, res) => res.json(books)); - Why use express.json() middleware?
✨ Show Answer
Answer: It parses incoming JSON bodies and attaches them to
req.body. Without it,req.bodyis undefined for JSON requests. - Add a 404 handler.
✨ Show Answer
app.use((req, res) => res.status(404).json({ error: "not found" })); - Read process arguments.
✨ Show Answer
// node app.js hello world console.log(process.argv.slice(2)); // ["hello", "world"] - Use fs/promises to read a JSON file (sketch).
✨ Show Answer
import { readFile } from "node:fs/promises"; const cfg = JSON.parse(await readFile("config.json", "utf8")); - Why is Node single-threaded yet good for I/O?
✨ Show Answer
Answer: Node uses libuv to push I/O onto a thread pool while JS itself runs single-threaded. Your code never blocks waiting for disk or network — callbacks/Promises fire when the result arrives. Result: thousands of concurrent connections handled by one process.
- Build an Express middleware that logs request time.
✨ Show Answer
app.use((req, res, next) => { const t0 = Date.now(); res.on("finish", () => console.log(req.method, req.url, Date.now() - t0, "ms")); next(); }); - Add CORS headers manually (sketch).
✨ Show Answer
app.use((req, res, next) => { res.setHeader("Access-Control-Allow-Origin", "*"); res.setHeader("Access-Control-Allow-Methods", "GET,POST,PATCH,DELETE"); res.setHeader("Access-Control-Allow-Headers", "Content-Type,Authorization"); if (req.method === "OPTIONS") return res.sendStatus(204); next(); }); - Build a runnable validate(body) helper for {text required}.
✨ Show Answer
a10.jsconst validate = b => { if (!b || typeof b.text !== "string" || !b.text.trim()) return { ok: false, error: "text required" }; return { ok: true }; }; console.log(validate({})); console.log(validate({ text: "hi" })); - Why do production apps put Node behind a reverse proxy (nginx)?
✨ Show Answer
Answer: nginx terminates TLS, serves static files faster, gzip-compresses, rate-limits, and load-balances multiple Node processes. It's also far better at gracefully handling slow clients than Node's built-in HTTP server.
- Add a graceful shutdown on SIGINT (sketch).
✨ Show Answer
const server = app.listen(3000); process.on("SIGINT", () => { console.log("shutting down"); server.close(() => process.exit(0)); }); - Demonstrate an in-memory rate-limiter (logic only).
✨ Show Answer
a12.jsfunction limiter(maxPerMin) { const hits = new Map(); return ip => { const now = Date.now(); const arr = (hits.get(ip) || []).filter(t => now - t < 60_000); arr.push(now); hits.set(ip, arr); return arr.length <= maxPerMin; }; } const ok = limiter(2); console.log(ok("ip1"), ok("ip1"), ok("ip1")); - In one paragraph, why is Node so popular for backends in 2026?
✨ Show Answer
Answer: One language end-to-end, the largest package ecosystem (npm), brilliant async I/O performance, and a tiny memory footprint per connection. Hire one engineer, they ship both ends. The big trade-off — single-threaded CPU per process — is worked around with worker threads or cluster mode when needed.
Summary — Module 38
Node = V8 + libuv + a rich standard library. Built-in http for raw work; Express for ergonomics. Compose with middleware. Read config from env vars. Don't commit .env. Behind a reverse proxy in production.