Node Exercises
Three short Node drills - HTTP server with routing, streamed CSV transform, and a typed shell helper.
Three short challenges
EXAMPLE
// 1. HTTP server with method-based routing - no framework
import { createServer } from 'http';
import { URL } from 'url';
type Handler = (req: any, res: any, params: Record<string, string>) => Promise<void> | void;
type Route = { method: string; pattern: RegExp; keys: string[]; handler: Handler };
const routes: Route[] = [];
const route = (method: string, path: string, handler: Handler) => {
const keys: string[] = [];
const pattern = new RegExp('^' + path.replace(/:([\w]+)/g, (_, k) => { keys.push(k); return '([^/]+)'; }) + '$');
routes.push({ method, pattern, keys, handler });
};
route('GET', '/healthz', (_, res) => res.end('ok'));
route('GET', '/users/:id', async (_, res, params) => {
res.writeHead(200, { 'content-type': 'application/json' });
res.end(JSON.stringify({ id: params.id, name: 'Ada' }));
});
createServer(async (req, res) => {
const url = new URL(req.url!, 'http://x');
for (const r of routes) {
if (r.method !== req.method) continue;
const m = url.pathname.match(r.pattern);
if (!m) continue;
const params = Object.fromEntries(r.keys.map((k, i) => [k, decodeURIComponent(m[i + 1])]));
return await r.handler(req, res, params);
}
res.writeHead(404); res.end('not found');
}).listen(3000);
// 2. Stream a CSV, transform, write a CSV
import { createReadStream, createWriteStream } from 'fs';
import { Transform } from 'stream';
import { pipeline } from 'stream/promises';
const upper = new Transform({
transform(chunk, _, cb) {
// chunk is a Buffer of partial CSV; for real CSVs use papaparse / csv-parse
cb(null, chunk.toString().toUpperCase());
},
});
await pipeline(
createReadStream('in.csv', { encoding: 'utf8' }),
upper,
createWriteStream('out.csv')
);
// 3. Typed shell helper
import { spawn } from 'child_process';
type RunResult = { code: number; stdout: string; stderr: string };
function run(cmd: string, args: string[] = [], opts: { input?: string; cwd?: string } = {}): Promise<RunResult> {
return new Promise((resolve, reject) => {
const p = spawn(cmd, args, { cwd: opts.cwd, stdio: ['pipe', 'pipe', 'pipe'] });
let stdout = '', stderr = '';
p.stdout.on('data', (d) => stdout += d);
p.stderr.on('data', (d) => stderr += d);
p.on('error', reject);
p.on('close', (code) => resolve({ code: code ?? -1, stdout, stderr }));
if (opts.input != null) { p.stdin.end(opts.input); }
});
}
const { stdout } = await run('node', ['-e', 'console.log(process.version)']);
console.log(stdout.trim());
// Stretch
// - Add timeout to the shell helper
// - Add gzip compression to the CSV pipeline
// - Add request-scoped logging to the HTTP server
Why it matters
These three drill the patterns most Node programs hit - HTTP routing without a framework, streams with backpressure, and shelling out safely. Together they unlock writing production-quality Node from scratch when you cannot reach for a library.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// Fill in the missing piece.
import { readFile } from 'node:____/promises';
Try it Yourself »
Discussion
Loading…