Frontend → Node Path
A five-stage ramp for frontend engineers becoming Node SMEs: event loop, streams, first API with tests, and a week-two capstone PR.
Recipe
Quick-reference recipe card - copy-paste ready.
Week 1 learning path (frontend → Node)
Day 1-2 Event loop + non-blocking IO ([How Node.js Works](../node-fundamentals/how-node-js-works/how-node-js-works.md))
Day 3 First Fastify route + Zod + curl smoke
Day 4 node:test integration test with inject()
Day 5 Capstone: POST /items with Postgres + migrationWhen to reach for this:
- React/Vue engineer joining backend rotation
- Full-stack hire strong on UI, weak on Postgres transactions
- Staff engineer designing onboarding curriculum
Stage 1: Event Loop (Not Threads)
Frontend devs know the browser single thread. Node is similar with a twist: libuv thread pool for some IO.
// BAD: blocks event loop ~2s on large sync work
import fs from "node:fs";
const data = fs.readFileSync("./huge.json", "utf8");
JSON.parse(data);
// GOOD: async + stream or worker
import fs from "node:fs/promises";
const data = await fs.readFile("./huge.json", "utf8");| Browser | Node server |
|---|---|
fetch in component | await fetch or DB query in handler |
| UI freezes on long sync work | All clients stall on loop block |
| Web Workers | BullMQ worker process |
- Exercise: add timing logs before/after sync
bcrypt.hashSyncvs asyncbcrypt.hashunder concurrentcurl
Related: How Node.js Works
Stage 2: Streams (Files and HTTP)
import { createReadStream, createWriteStream } from "node:fs";
import { pipeline } from "node:stream/promises";
import { createGzip } from "node:zlib";
await pipeline(
createReadStream("export.csv"),
createGzip(),
createWriteStream("export.csv.gz")
);- Frontend
FileReaderloads to memory; Node streams bound RSS on uploads/downloads - Fastify/Express multipart routes should stream to disk or S3, not
buffer entire file
Stage 3: First API with Tests
// test/items.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
import { buildApp } from "../src/app.js";
test("GET /items returns array", async () => {
const app = await buildApp();
const res = await app.inject({ method: "GET", url: "/items" });
assert.equal(res.statusCode, 200);
assert.ok(Array.isArray(res.json()));
await app.close();
});inject()is like Testing Library for HTTP - no browser, no open port- Frontend Jest knowledge transfers; syntax is
node:test+assert
Related: Fastify Basics | Testing Fastify Apps
Stage 4: Async Errors and Transactions
// WRONG: floating promise in fire-and-forget
sendWelcomeEmail(user.id);
// RIGHT: await or explicit queue
await emailQueue.add("welcome", { userId: user.id });await db.transaction(async (tx) => {
await tx.order.create({ data: order });
await tx.inventory.decrement({ where: { sku }, data: { qty: 1 } });
});- Unhandled rejections crash Node 24 by policy in many teams
- DB transactions are new for frontend hires - pair on first
transaction()block
Stage 5: Capstone PR (Week One)
## Capstone: Items API slice
- [ ] Migration: items table
- [ ] GET /items, POST /items with Zod
- [ ] node:test coverage for 400 and 201
- [ ] curl smoke in PR description
- [ ] No secrets in diff- Merge capstone before assigning production backlog tickets
- Review with Pairing on API PRs checklist
Mindset Shifts
| Frontend habit | Node adjustment |
|---|---|
| Instant feedback in browser | Read logs and curl, not hot reload only |
| Client-side validation enough | Server must validate with Zod |
| State in React context | State in Postgres + cache invalidation |
| Deploy static assets | Deploy API + migration order matters |
FAQs
Express or Fastify for learning?
Fastify 5 for new hires: schema-first, inject() tests, Pino built in. Express if the team production standard is Express - align with ADR.
How deep on Postgres week one?
CRUD + one migration + transaction example is enough. ORM depth comes week 2-3 with mentor pairing.
Related
- Onboarding Basics - machine setup
- Skills Matrix - junior competencies
- Node.js Basics - first commands
- zod - validation at boundaries
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.