---
status: accepted
date: 2026-04-29
decision-makers: R&D Lead
tags: [backend]
---

# Backend stack — NestJS over Express, Fastify, Hono

## Context and Problem Statement

The backend (`portal-bff`) is the BFF: it owns authentication, sessions, and downstream API orchestration; it persists business data; it must be observable and auditable. The frontend never holds tokens or talks directly to upstream APIs. The choice of HTTP framework has architectural ripple effects (DI shape, validation pipeline, security defaults, ecosystem of integrations).

Which Node TypeScript framework gives us the right combination of structure, ecosystem, performance, and longevity for a security-sensitive BFF in an enterprise context?

## Decision Drivers

- Security is a first-class concern — the framework must impose structure rather than leave it to be hand-built.
- Architectural alignment with the Angular frontend (DI, decorators, modules, RxJS).
- First-class ecosystem for the things we will need: Passport / MSAL, Pino logging, OpenTelemetry, Prisma, Redis, validation, health checks.
- Long-term enterprise viability (no pre-1.0, no single-maintainer projects).
- Performance acceptable for a BFF (request mediation, not raw throughput benchmarks).

## Considered Options

- **NestJS** (latest stable major). (Chosen.)
- **Express 5** — minimal, ubiquitous.
- **Fastify 5** — performance-oriented, plugin-based.
- **Hono** — modern, multi-runtime.
- **tRPC** — typed RPC over HTTP.

## Decision Outcome

Chosen option: **NestJS at the latest stable major**, mounted on the **Express adapter** by default. Migration to the Fastify adapter remains an option later if profiling justifies it; this is a configuration change, not an application rewrite, because both adapters are first-class in NestJS.

### Consequences

- Good, because NestJS imposes a clear, opinionated architecture (modules, providers, controllers, pipes, guards, interceptors) — minimal room for bricolage.
- Good, because the team lives in the same paradigm as Angular (DI, decorators, RxJS).
- Good, because every supporting concern has a first-class NestJS integration: `@nestjs/passport` + `@azure/msal-node` for auth, `nestjs-pino` for logs, `nestjs-cls` for AsyncLocalStorage, `@nestjs/terminus` for health checks, `nestjs-prisma` for persistence.
- Good, because NestJS is at v11+ with a healthy enterprise track record (Adidas, Roche, Capgemini publicly), an active core team, and commercial support options.
- Good, because the adapter pattern lets us swap to Fastify later without touching application code.
- Bad, because NestJS has a non-trivial conceptual surface (DI hierarchy, lifecycle hooks, request-scoped providers) — one-time learning cost.
- Bad, because the framework is heavier than Express or Fastify alone — acceptable for our profile.

### Confirmation

- `apps/portal-bff/package.json` depends on `@nestjs/core`, `@nestjs/common`, `@nestjs/platform-express`.
- `apps/portal-bff/src/main.ts` bootstraps via `NestFactory.create`.
- All HTTP entry points are `@Controller`-based; no raw Express routes outside framework escape hatches.
- A global `ValidationPipe` is applied with `whitelist: true, forbidNonWhitelisted: true, transform: true`.

## Pros and Cons of the Options

### NestJS (chosen)

- Good, because opinionated structure sets a clear bar across the team.
- Good, because alignment with Angular reduces cognitive distance.
- Good, because the ecosystem covers every BFF concern we have.
- Bad, because verbose for tiny services — acceptable here, this is a real BFF.

### Express 5

- Good, because the lowest common denominator — every Node engineer knows it.
- Bad, because minimalist by design — every cross-cutting concern (DI, validation, structured errors, lifecycle) is built by hand. For a security-sensitive BFF in an "anti-bricolage" project, this is a poor fit.
- Bad, because Express-as-foundation in 2026 is the legacy default; the modern center of gravity has moved.

### Fastify 5

- Good, because excellent raw performance and a clean plugin model.
- Good, because schema-driven validation is built in.
- Bad, because architecture is left to the application — we'd reinvent NestJS poorly.
- Note: NestJS can run on a Fastify adapter — we can have both if a perf profile demands it.

### Hono

- Good, because modern, minimal, multi-runtime (Node, Bun, Workers).
- Bad, because too young (~2023) for our longevity bar — limited enterprise track record.
- Status: keep in watch list; reconsider in 12–24 months.

### tRPC

- Good, because end-to-end type safety from server to client without code generation.
- Bad, because it tightly couples frontend and backend builds — incompatible with a BFF that may also need to serve non-Angular consumers later (downstream API gateways, mobile, partner integrations).
- Bad, because not a general HTTP framework — orthogonal to the choice we're making.

## More Information

- NestJS documentation: https://docs.nestjs.com
- NestJS Fastify adapter: https://docs.nestjs.com/techniques/performance
- Related ADRs: [ADR-0002](0002-adopt-nx-monorepo-apps-preset.md), [ADR-0006](0006-persistence-postgresql-prisma.md), ADR-0008 (auth flow, future), ADR-0009 (sessions, future), ADR-0012 (observability, future).
