All guides

Node.js and TypeScript

Practical guidance for TypeScript configuration, Node.js runtime behavior, package boundaries, errors, testing, and maintainable services.

100 articles in this section.

Node.js 26.8.1 upgrade checklist: what to test before moving a backend

A practical Node.js 26.8.1 upgrade checklist for backend teams, covering runtime compatibility, native dependencies, tests, observability, and rollback planning.

Handle unhandled rejections in Node.js without pretending recovery

An unhandled rejection can leave unknown application state, so log structured context, begin graceful shutdown, and fix the missing ownership path.

Undici connection pools: avoid socket explosions in Node.js

Outbound HTTP needs bounded pools per origin so traffic spikes do not create unlimited sockets or hide slow dependencies behind queues.

HTTP keep-alive tuning in Node.js behind a load balancer

Keep-alive timeouts must align across client, server, proxy, and load balancer or reused sockets will fail at awkward boundaries.

Node.js stream backpressure: stop buffering the whole response

Backpressure means a producer slows down when the consumer cannot accept more data, preventing memory growth under slow clients.

Cancel child processes safely with AbortController in Node.js

Canceling the parent promise is not enough; the child process must receive a signal, respect a deadline, and be reaped without leaving descendants.

Worker threads for CPU-heavy Node.js work: when they help

Worker threads help CPU-bound JavaScript by moving computation off the event loop, but transfer cost and memory can erase the benefit for tiny jobs.

Enforce coverage thresholds with node:test

Coverage thresholds are useful as a regression floor, not proof that behavior is tested or assertions are meaningful.

Mock timers with the Node.js test runner without flaky sleeps

Mocked time makes expiry and retry tests deterministic when application code reads time through supported APIs rather than waiting in real time.

Node.js permission model: reduce what a compromised process can reach

Process permissions add defense in depth by restricting files, child processes, workers, and other capabilities the application does not need.

AsyncLocalStorage in Node.js: request context without parameter chains

AsyncLocalStorage can carry request IDs and tenant context through asynchronous work, but context must start at a trusted boundary and never replace authorization checks.

TypeScript satisfies vs annotation: preserve useful inference

The satisfies operator checks compatibility while preserving the expression’s narrower inferred type, unlike some broad annotations.

Declaration maps in TypeScript: debug the source, not dist files

Declaration maps let editor navigation return to original TypeScript source, but published paths must match the package contents.

TypeScript project references: when incremental builds go stale

Project references speed large repositories only when dependency direction, outputs, and build info files stay consistent.

TypeScript package exports: expose types and runtime together

A package should expose matching type and runtime entry points so consumers do not resolve declarations from a path Node cannot execute.

NodeNext module resolution: why file extensions matter

NodeNext follows Node’s ESM rules, so relative imports in emitted code need runtime-valid extensions even when the source file is TypeScript.

TypeScript verbatimModuleSyntax: type imports without surprises

Verbatim module syntax keeps imports and exports closer to what you wrote, making type-only intent explicit and exposing module mismatches earlier.

TypeScript exactOptionalPropertyTypes: missing is not undefined

With exact optional properties, an absent key and a present key holding undefined are different states, which matters for patches and serialization.

TypeScript noUncheckedIndexedAccess: fix unsafe lookups clearly

This option makes array and dictionary access include undefined, revealing assumptions that need a guard, a default, or a stronger data model.

Benchmark TypeScript 7 correctly: cold build vs editor latency

A single tsc timing misses the experience developers feel, so measure clean builds, incremental builds, editor startup, and completion latency separately.

TypeScript 7 migration checklist for a Node.js monorepo

Treat the TypeScript 7 upgrade as a compiler and tooling migration: capture the TypeScript 6 baseline, remove deprecated options, then compare diagnostics package by package.

Graceful shutdown in Node.js: drain HTTP, queues, and database work

Closing the HTTP listener is only the first step; a process must stop taking work, finish bounded in-flight tasks, and then release.

Server-Sent Events in Node.js: production reconnects and backpressure

SSE is simple for one-way updates, but production code must handle proxy buffering, disconnect cleanup, event IDs, and slow.

Node.js diagnostics_channel: add observability without patching libraries

diagnostics_channel provides low-coupling instrumentation points, but subscribers must remain cheap and protect sensitive.

Node.js July 2026 security update: versions, risks, and rollout plan

Node.js fixed high-severity HTTP/2 and Permission Model flaws on July 29. Use this practical guide to select versions, test production risks, and deploy safely.

TypeScript 7 upgrade guide: migrate without breaking your toolchain

A practical TypeScript 7 migration plan covering TypeScript 6 compatibility, compiler speed, editor setup, CI checks, and tools that still need the old API.

Node.js 26 upgrade rollback plan for APIs and workers

Node.js 26 includes engine, HTTP, API, and removal changes that can affect several application layers. Pin the old image, canary one service, compare telemetry, and make schema changes backward compatible.

Test native Node.js addons before upgrading to Node 26

A major Node and V8 update can require rebuilt binaries or updated addon releases. Test clean installs for every architecture, prefer Node-API packages, and build the production container in CI.

Should production use Node.js 26 Current before LTS?

Node.js 26 is the Current line and is scheduled to enter LTS in October 2026. Use Current for evaluation and controlled services; keep critical production on a supported LTS unless a verified benefit justifies earlier adoption.

Node.js 26 V8 14.6 upgrade: what backend teams should test

Node.js 26 moves to V8 14.6 from the Chromium 146 line. Benchmark startup, hot endpoints, memory, worker threads, and native dependencies on production-shaped traffic.

Node.js 26 removes legacy _stream modules: dependency audit guide

Node.js 26 removes legacy `_stream_wrap`, `_stream_readable`, `_stream_writable`, and related internal modules. Scan the dependency tree and upgrade packages before changing the production runtime.

Node.js 26 removes writeHeader: migrate to writeHead

Node.js 26 fully removes `http.ServerResponse.prototype.writeHeader()` and directs users to `writeHead()`. Search application and dependency code, replace direct calls, and test response status and headers.

Undici 8 in Node.js 26: fetch migration and regression checklist

Node.js 26 updates Undici to version 8.0.2. Test redirects, aborts, proxy use, streaming bodies, connection reuse, and timeout policy against real services.

Iterator.concat in Node.js 26: combine lazy sequences without arrays

V8 14.6 in Node.js 26 includes iterator sequencing through `Iterator.concat()`. Use lazy concatenation when inputs are ordered iterables and consumers can process incrementally.

Node.js 26 Map getOrInsert: practical cache initialization examples

Node.js 26 ships V8 14.6 with `Map.prototype.getOrInsert` and `getOrInsertComputed` support. Use computed initialization for per-key objects and keep compatibility checks for older LTS environments.

TypeScript 6 function inference changes: fix order-sensitive generic errors

TypeScript 6 changes context sensitivity for functions without `this`, reducing some order-dependent inference behavior. Add an explicit type argument or variable annotation at the unstable boundary.

TypeScript 6 DOM iterable merge: simplify the lib array

TypeScript 6 folds DOM iterable and async-iterable declarations into `lib.dom.d.ts`; the separate entries remain empty compatibility files. Remove redundant entries and type-check browser, worker, and server configs separately.

Map getOrInsert in TypeScript 6: replace repeated cache initialization

TypeScript 6 includes types for the standardized upsert methods `getOrInsert` and `getOrInsertComputed` in appropriate libraries. Use the computed form for expensive defaults and verify runtime support before removing a helper.

Fix TypeScript TS5112 when compiling one file beside tsconfig.json

TypeScript 6 reports TS5112 and requires `--ignoreConfig` when file arguments intentionally bypass `tsconfig.json`. Use `tsc -p` for project checks and reserve `--ignoreConfig` for isolated experiments.

TypeScript 6 no-default-lib directive removal: use noLib correctly

TypeScript 6 no longer supports `/// <reference no-default-lib="true"/>`. Use `noLib` or explicit `lib` configuration at the project boundary.

Import assertions to import attributes in TypeScript 6

TypeScript 6 deprecates import assertions and directs projects to the `with` keyword. Update source, runtime versions, tests, and bundler support together.

TypeScript 6 removes outFile: migrate to a real bundler

TypeScript 6 removes the `outFile` compiler option. Use TypeScript for checking and declaration emit, then bundle JavaScript with Vite, Rollup, esbuild, or another maintained tool.

TypeScript 6 alwaysStrict false removal: find sloppy-mode code safely

TypeScript 6 assumes strict semantics and no longer allows `alwaysStrict: false`. Search emitted JavaScript assumptions and run behavior tests before deleting the setting.

TypeScript 6 esModuleInterop is always enabled: import migration examples

TypeScript 6 no longer permits `esModuleInterop` or `allowSyntheticDefaultImports` to be false. Remove the false settings and test default imports from CommonJS dependencies.

TypeScript 6 removes moduleResolution classic: modern replacement guide

TypeScript 6 removes `moduleResolution: classic` rather than only warning about it. Choose `NodeNext` for direct Node execution or `Bundler` when a bundler resolves imports.

TypeScript 6 removes AMD, UMD, SystemJS, and module none: what to use instead

TypeScript 6 no longer supports `module` values `amd`, `umd`, `systemjs`, or `none`. Emit ESM or CommonJS for the real runtime and let a maintained bundler create distribution formats.

TypeScript 6 downlevelIteration deprecation: remove a setting that no longer helps

Because TypeScript 6 deprecates the ES5 target, setting `downlevelIteration` now produces a deprecation error. Remove the option after confirming the project targets ES2015 or newer.

TypeScript 6 deprecates target ES5: migration options for legacy browsers

TypeScript 6 deprecates `target: es5` and recommends a newer target or an external transformation step when ES5 remains mandatory. Measure actual browser support, move TypeScript to ES2015 or newer, and use a dedicated transpiler only for verified le

TypeScript 6 libReplacement false: understand the performance change

TypeScript 6 sets `libReplacement` to false by default to avoid unnecessary failed resolutions and watcher overhead. Leave it disabled unless the project deliberately supplies replacement lib packages.

TypeScript 6 noUncheckedSideEffectImports default: fix hidden import typos

TypeScript 6 enables `noUncheckedSideEffectImports` by default so unresolved side-effect-only imports are reported. Fix the path or add a narrow ambient module declaration for intentionally loader-managed assets.

Node.js 26 readFile caller-supplied buffers: when it helps

Understand Node.js 26.4 caller-supplied readFile buffers, possible allocation benefits, buffer sizing risks, and how to benchmark the change.

Node.js node:vfs explained: what the new virtual filesystem can do

A cautious introduction to Node.js 26.4 node:vfs, mounted virtual filesystems, fs/promises dispatch, testing opportunities, and stability concerns.

Node.js 26 package maps explained: a new loader capability

Understand Node.js 26.4 package maps, how they differ from package imports and exports, and why production teams should treat them as experimental.

RegExp.escape in TypeScript 6: safely building regular expressions

Use the new RegExp.escape type support in TypeScript 6 to safely insert user text into regular expressions without changing pattern meaning.

TypeScript 6 Temporal types: typing Node.js 26 date and time code

Use TypeScript 6 Temporal types with Node.js 26, configure the right lib, and avoid confusing type availability with runtime support.

TypeScript 6 allows Bundler resolution with CommonJS: when to use it

Understand TypeScript 6 support for moduleResolution Bundler with CommonJS output and decide whether it matches your build pipeline.

TypeScript 6 supports #/ subpath imports: package imports explained

Use TypeScript 6 support for #/ package subpath imports, align package.json imports, and avoid aliases that work only in the editor.

TypeScript 6 stableTypeOrdering: reproducible declaration output explained

Understand TypeScript 6 stableTypeOrdering, when deterministic union and intersection ordering matters, and its performance tradeoff.

TypeScript 6 deprecates moduleResolution node: migrate from node10 safely

Move from deprecated moduleResolution node/node10 to NodeNext or Bundler without creating package exports and file-extension bugs.

TypeScript 6 baseUrl deprecation: migrate path aliases safely

Fix the TypeScript 6 baseUrl deprecation with before-and-after tsconfig examples, then verify aliases in Node, bundlers, tests, and declaration output.

TypeScript 6 target es2025 default: should you set target explicitly?

Decide whether to accept TypeScript 6’s floating ES2025 target or pin a JavaScript target for Node, browsers, libraries, and reproducible builds.

TypeScript 6 module defaults to esnext: CommonJS migration checklist

Understand the TypeScript 6 module default change and keep CommonJS projects aligned with package.json, Node, and emitted JavaScript.

TypeScript 6 strict true by default: what breaks and how to upgrade

Prepare for TypeScript 6 strict mode becoming the default, find newly unsafe paths, and migrate without disabling useful checks globally.

TypeScript 6 types defaults to empty: fixing missing Node globals

Fix missing process, Buffer, and Node module types after TypeScript 6 by declaring the runtime type packages your project actually uses.

TypeScript 6 rootDir default change: why dist/src suddenly appears

Fix TypeScript 6 output moving into dist/src by setting rootDir explicitly and checking files included outside the source directory.

How to fix ERR_MODULE_NOT_FOUND in Node.js ESM

Fix Node.js ERR_MODULE_NOT_FOUND errors by checking file extensions, package exports, aliases, build output, and ESM resolution in the right order.

TypeScript monorepo tsconfig setup without confusing everyone

How to structure tsconfig files in a TypeScript monorepo with base config, package configs, build configs, and fewer editor surprises.

TypeScript project references explained for growing codebases

A plain-English guide to TypeScript project references, faster builds, boundaries, monorepos, and when the extra config is worth it.

Node.js test runner explained: when you can skip Jest or Vitest

A simple guide to the built-in Node.js test runner, where it fits, and when a project still benefits from Jest, Vitest, or Playwright.

Node.js Permission Model explained: what it protects and what it does not

A practical guide to the Node.js Permission Model, filesystem access, child process restrictions, and realistic backend security expectations.

TypeScript module node20 explained: when should you use it?

A practical guide to TypeScript module node20, Node.js module resolution, ESM/CommonJS interop, and backend tsconfig choices.

TypeScript erasableSyntaxOnly explained for Node.js type stripping

Understand TypeScript erasableSyntaxOnly, which syntax it rejects, and the tsconfig needed when Node.js runs TypeScript by stripping types without compiling.

TypeScript 6.0 explained: what changes matter before TypeScript 7

A practical explanation of TypeScript 6.0 as a transition release, what to check before upgrading, and how to prepare for TypeScript 7.

require(esm) in Node.js: what CommonJS teams should understand

A practical guide to Node.js ESM and CommonJS interop, why require(esm) matters, and how teams should migrate without chaos.

Node.js security releases explained: what backend developers should actually do

How to respond to Node.js security releases, including LTS updates, dependency checks, CI testing, and production rollout.

Node.js Temporal API explained: why dates may finally get less painful

A practical explanation of the Temporal API in Node.js 26, how it differs from Date, and what developers should know about time zones.

TypeScript 5.9 import defer explained: what problem does it solve?

A simple explanation of TypeScript 5.9 import defer, when deferred module evaluation helps, and when normal imports are still better.

Node.js 26 vs Node.js 24 LTS: should you upgrade now?

A practical guide to choosing between Node.js 26 Current and Node.js 24 LTS for backend apps, side projects, and production systems.

Redis caching mistakes Node.js developers keep making

Common Redis caching mistakes in Node.js apps, including stale data, missing TTLs, cache stampedes, and unsafe keys.

Request ID logging in Node.js: debug production without guessing

How request IDs connect logs across routes, services, queues, and errors in a Node.js backend.

API error response format for Node.js apps

How to design API error responses that are consistent, debuggable, and safe for frontend teams and users.

Retry and backoff patterns in Node.js: avoid making outages worse

How to use retries, exponential backoff, jitter, and max attempts in Node.js without creating duplicate side effects.

JWT vs sessions for Node.js apps: which auth should you choose?

A practical comparison of JWTs and server sessions for Node.js authentication, including security, revocation, and frontend needs.

How to structure a Node.js backend project without overengineering

A practical Node.js backend folder structure that keeps routes, services, repositories, jobs, and config understandable.

How to migrate a JavaScript project to TypeScript safely

A step-by-step migration plan for moving a JavaScript project to TypeScript without stopping feature work.

noUncheckedIndexedAccess explained: why arrays suddenly look unsafe

What noUncheckedIndexedAccess does in TypeScript, why it creates more undefined checks, and when it is worth enabling.

TypeScript generics without confusion: a practical mental model

A simple way to understand TypeScript generics using arrays, API responses, reusable helpers, and constraints.

Zod validation at API boundaries in TypeScript

How to use schema validation at API boundaries so TypeScript types match real runtime data.

How to type and validate environment variables in Node.js

Validate Node.js environment variables once at startup and expose a typed TypeScript config, with Zod and dependency-free patterns plus test examples.

Type guards in TypeScript: when validation becomes readable

How to write useful TypeScript type guards for unknown data without turning your code into unreadable type tricks.

Discriminated unions in TypeScript explained with API states

How discriminated unions make loading, success, error, and empty states easier to model in TypeScript.

unknown vs any in TypeScript: the difference that prevents bugs

Why unknown is safer than any, how to narrow unknown values, and where any is still acceptable in TypeScript projects.

TypeScript satisfies operator explained with practical examples

A clear explanation of the TypeScript satisfies operator, when it is better than type annotations, and how it helps config objects.

TypeScript path aliases explained: baseUrl, paths, and runtime bugs

How TypeScript path aliases work, why they can fail at runtime, and how to set them up without confusing your bundler or Node.js.

How to fix npm install errors: ERESOLVE, ENOENT, and permission denied

Fix npm install errors by identifying ERESOLVE, ENOENT, EACCES, ETARGET, and network failures before changing lockfiles or dependency versions.

How to fix Cannot use import statement outside a module in Node.js

A step-by-step fix for the Node.js import statement error, with examples for ES modules, CommonJS, TypeScript, file extensions, and package.json.

BullMQ vs Bull vs raw Redis queues for Node.js

A practical BullMQ vs Bull vs raw Redis comparison with examples for background jobs, retries, delayed work, and when not to build your own queue.

Idempotency in Node.js workers: how to avoid charging a user twice

A backend pattern for making Node.js jobs safe to retry, with examples for payment jobs, idempotency keys, Redis claims, and stored state.

What strict: true actually checks in TypeScript

A simple explanation of what TypeScript strict mode catches, with examples for null checks, implicit any, this binding, class fields, and errors.

tsconfig.json explained: the TypeScript options that actually matter

A plain-English guide to the tsconfig options that affect real projects: target, module, moduleResolution, strict, lib, include, and noEmit.