Backend development: Node.js

Sefism.

Chapter 01

Node.js and TypeScript essentials

How Node.js runs your code, how TypeScript describes it and how packages and frameworks fit together. Everything later in the sheet builds on these.

The event loop

What
Node.js runs your JavaScript on one thread. The event loop picks up callbacks when I/O, timers or promises are ready, while libuv does the actual network and file work in the background.
Why
It lets one process serve thousands of concurrent connections cheaply, as long as each callback finishes quickly. Promise callbacks (microtasks) always run before the next timer.
TypeScript
console.log('1 sync');
setTimeout(() => console.log('4 timer'), 0);
Promise.resolve().then(() => console.log('3 microtask'));
console.log('2 sync');

Watch out

CPU-heavy work (big JSON parsing, image resizing, sync crypto) blocks every request; move it to worker_threads or a separate job.

Promises and async/await

Also called Promise, async, await

What
A Promise represents a value that arrives later. async functions return a Promise, and await pauses the function until it settles, without blocking the thread.
Why
Almost every backend call (database, HTTP, Redis) is async. Promise.all runs independent calls concurrently instead of one after another.
TypeScript
const [user, orders] = await Promise.all([
  userRepo.findById(id),
  orderRepo.forUser(id),
]);

Watch out

Forgetting await (the error escapes your try/catch) or awaiting inside a loop when the calls could run together.

TypeScript types and interfaces

Also called TS, TypeScript

What
TypeScript adds static types to JavaScript: interfaces and type aliases describe object shapes, unions list allowed values, and the compiler checks every use. Enable strict mode.
Why
Types catch wrong field names, missing null checks and bad refactors before the code runs, and make a large codebase navigable.
TypeScript
type Role = 'admin' | 'member';

interface User {
  id: string;
  email: string;
  role: Role;
  deletedAt?: Date;
}

Watch out

Types vanish at runtime: a request body typed as User is still unchecked JSON until you validate it.

npm, package.json and lockfiles

Also called npm, package-lock.json, pnpm

What
package.json lists dependencies and scripts; the lockfile pins the exact version of every package in the tree. npm, pnpm and Yarn all read them.
Why
The lockfile makes installs reproducible, so CI and production run the same code you tested. npm ci installs exactly what the lockfile says.
Shell
npm install express zod
npm install -D typescript vitest @types/express
npm ci            # clean, lockfile-exact install for CI
npm run build
npm audit

Watch out

Not committing the lockfile, or running npm install in CI, so a new minor version sneaks into production.

NestJS controllers, providers and modules

Also called NestJS, Nest

What
NestJS is a structured framework on top of Express or Fastify. Controllers handle routes, providers (services) hold logic, and modules group them; Nest injects dependencies through constructors.
Why
It gives large TypeScript codebases one consistent layout, with built-in pipes for validation, guards for auth and easy testing through injected fakes.
TypeScript
@Controller('users')
export class UsersController {
  constructor(private readonly users: UsersService) {}

  @Get(':id')
  async findOne(@Param('id', ParseIntPipe) id: number) {
    const user = await this.users.findOne(id);
    if (!user) throw new NotFoundException();
    return user;
  }
}

Watch out

Forgetting to list a provider in its module, which fails at startup with "Nest cannot resolve dependencies".

JavaScript core: values, scope and closures

Also called closures, let and const, references, JavaScript

What
Primitives (string, number, boolean, null, undefined) are copied, while objects are passed by reference. let and const are block scoped, and a closure is a function that keeps access to the variables where it was defined.
Why
Backend bugs often come from shared object references, == coercion or a closure capturing the wrong value, so these basics come before Express or NestJS.
TypeScript
const user = { name: 'Ali' };
const same = user;          // same object, not a copy
same.name = 'Sara';         // user.name is now 'Sara'
const clone = { ...user };  // shallow copy

function counter() {
  let n = 0;
  return () => ++n;         // the closure keeps n alive
}

Watch out

Using == instead of ===, so '0' == 0 is true.

01 · Node.js and TypeScript essentials1 / 10