NestJS Module 1 – Getting started with NestJS 12
What NestJS is and when to choose it
What NestJS adds on top of Express or Fastify, what it costs, and how to choose between Nest, Express, Fastify and Hono for a team and a workload.
What you will learn
- Explain how Nest layers modules, dependency injection and decorators over Express or Fastify
- Compare one endpoint written with Express, Fastify and Nest by files, lines and behaviour
- Choose between Nest, Express, Fastify and Hono for a given team and workload
- Read npm's dist-tags and engines before choosing a Nest version
What you’ll learn
What you will learn
- Explain how Nest layers modules, dependency injection and decorators over Express or Fastify
- Compare one endpoint written with Express, Fastify and Nest by files, lines and behaviour
- Choose between Nest, Express, Fastify and Hono for a given team and workload
- Read npm's dist-tags and engines before choosing a Nest version
On this page
One POST /orders endpoint that checks its body and answers 201 or 400 takes 13 lines in one file with Express 5,
and 45 lines in six files with NestJS 12, plus a compile step. Those counts come from the three versions this lesson
runs side by side, and they raise the question it answers: what do the extra files give you, and when is Nest worth
them?
You will see what Nest adds on top of Express or Fastify, follow one request through a Nest app, send the same three requests to all three versions, choose between Nest, Express, Fastify and Hono for a team and a workload, and read npm’s release tags before you pick a version.
Nest runs on top of Express or Fastify
Nest is not an HTTP server of its own. At start-up it reads your classes, then registers one route for each handler
on an HTTP library underneath: Express 5 by default, or Fastify 5. That library parses requests and writes
responses; the package @nestjs/platform-express 12.1.2 depends on express 5.2.1, and @nestjs/platform-fastify
on Fastify. Nest calls this layer the HTTP adapter: The part of Nest that wraps an HTTP library, Express or Fastify. Nest registers its routes on the library through it, and the library parses requests and writes responses. @nestjs/platform-express is the default..
On top of the adapter, Nest adds four things that a plain Express app leaves to you:
- Modules. A module: A class marked with @Module() that lists the controllers and providers that belong together, the modules it imports and the providers it shares with them. Every Nest app starts from one root module. lists the controllers and providers that belong together, so a feature lives in one place and states what it shares.
- Dependency injection. A class asks for what it needs in its constructor, and Nest’s container builds it and passes it in. This is dependency injection: A way of building objects in which a class asks for what it needs in its constructor and a container creates those objects and passes them in, instead of the class creating them itself., and the classes it builds are provider: Anything Nest's container can create and inject, most often a class marked with @Injectable() such as a service. By default Nest creates one instance of each provider and shares it..
- Decorators for routes.
@Controller('orders')and@Post()on a class and a method tell Nest which requests the method handles; such a class is a controller: A class marked with @Controller() whose methods handle requests. Each method with a route decorator such as @Get() or @Post() becomes one route of the app.. - A pipeline around every handler. Middleware, guards, interceptors, pipes and exception filters run before and after your method, in a fixed order. A pipe: A class Nest runs on a handler's argument before the handler, to check it or turn it into another type. A pipe that throws stops the request with an error response., for example, checks an argument before the method sees it.
Step through one request of this lesson’s Nest version, a valid order, and watch which of these layers runs where:
The drawing shows the whole scene; its 8 steps are listed below.
Every step as text
- The request. A client sends POST /orders with the body {"kitchenId":"pune-07","meals":3}. No code of yours has run yet: the request first meets the HTTP server that Express owns.
- Express. Express 5 receives the request and parses the JSON body, because Nest registered Express's body parser when the app started. Express also adds the X-Powered-By header, which is why a Nest response says Express.
- Nest's router. At start-up Nest read the @Controller('orders') and @Post() decorators and registered one Express route for them. POST /orders matches it, so the request belongs to OrdersController.create.
- The pipe. Before the method runs, Nest runs the pipes of its parameters. StandardSchemaValidationPipe reads the schema that @Body({ schema }) attached and checks the body. meals is 3, a whole number from 1 to 10, so the body passes.
- A body that fails. With meals 11 the pipe throws instead, and Nest answers 400 with the problems listed, without calling your method. That is the side branch: the controller never sees invalid input.
- Your controller. Nest calls create(input) on the one OrdersController it built at start-up. The method holds no rule of its own; it hands the input to OrdersService.
- Your service. OrdersService was created once by Nest's container and passed to the controller's constructor. It gives the order its id and returns it.
- The response. The method returned an object, so Nest turns it into JSON, and because the method is a POST it answers 201 Created. Express writes the bytes back to the client.
Two things in that walk are easy to miss. Your controller never touched the socket: Express parsed the body and wrote the reply, Nest chose the method and ran the pipe. And the method never saw the invalid body of the side branch, because the pipe answered 400 first. The rest of this track teaches each of these layers in turn.
Checkpoint
Check: What Nest adds on top of Express
The same endpoint, written three ways
Here is POST /orders for a tiffin service, TiffinTrail: a kitchen id and a number of meals from 1 to 10. Express 5
leaves the check to you, so the handler tests each field by hand:
// POST /orders in Express 5: parse the JSON body, check it by hand, answer 201 or 400.
import express from 'express';
export const app = express();
app.use(express.json());
let nextId = 1;
app.post('/orders', (req, res) => {
const { kitchenId, meals } = req.body ?? {};
const valid = typeof kitchenId === 'string' && Number.isInteger(meals) && meals >= 1 && meals <= 10;
if (!valid) {
res.status(400).json({ message: 'kitchenId must be text and meals a whole number from 1 to 10' });
return;
}
res.status(201).json({ id: nextId++, kitchenId, meals });
}); Fastify 5 takes a JSON Schema on the route and checks the body before the handler runs:
// POST /orders in Fastify 5: a JSON Schema on the route, checked by Fastify before the handler runs.
import Fastify from 'fastify';
export const app = Fastify();
let nextId = 1;
const body = {
type: 'object',
required: ['kitchenId', 'meals'],
properties: {
kitchenId: { type: 'string' },
meals: { type: 'integer', minimum: 1, maximum: 10 },
},
};
app.post('/orders', { schema: { body } }, async (request, reply) => {
const { kitchenId, meals } = request.body;
reply.code(201);
return { id: nextId++, kitchenId, meals };
}); The Nest version splits the same work into a schema, a provider, a controller and a module, plus app.ts, which
switches on the validation pipe, and main.ts, which starts the server. The schema is a Zod object; Nest 12 checks
any Standard Schema: A common interface that validation libraries such as Zod, Valibot and ArkType implement, so that a framework can check data against a schema from any of them. Nest 12's StandardSchemaValidationPipe uses it. that @Body({ schema }) names once
StandardSchemaValidationPipe is on:
TypeScript (orders.controller.ts) · three-ways/nest/src/orders.controller.ts
import { Body, Controller, Post } from '@nestjs/common';
import { createOrderSchema, type CreateOrderInput } from './create-order.schema.js';
import { OrdersService } from './orders.service.js';
// The controller only translates HTTP: POST /orders becomes a call to OrdersService.create().
@Controller('orders')
export class OrdersController {
constructor(private readonly orders: OrdersService) {}
@Post()
create(@Body({ schema: createOrderSchema }) input: CreateOrderInput) {
return this.orders.create(input);
}
} TypeScript (orders.service.ts) · three-ways/nest/src/orders.service.ts
import { Injectable } from '@nestjs/common';
import type { CreateOrderInput } from './create-order.schema.js';
// The business rule lives in a provider, which Nest creates once and hands to every class that asks for it.
@Injectable()
export class OrdersService {
private nextId = 1;
create(input: CreateOrderInput) {
return { id: this.nextId++, ...input };
}
} TypeScript (create-order.schema.ts) · three-ways/nest/src/create-order.schema.ts
import { z } from 'zod';
// The rule for the body, written once: Nest checks it, and TypeScript derives the type from it.
export const createOrderSchema = z.object({
kitchenId: z.string(),
meals: z.number().int().min(1).max(10),
});
export type CreateOrderInput = z.infer<typeof createOrderSchema>; TypeScript (app.module.ts) · three-ways/nest/src/app.module.ts
import { Module } from '@nestjs/common';
import { OrdersController } from './orders.controller.js';
import { OrdersService } from './orders.service.js';
// The module lists what belongs together: Nest builds the service and gives it to the controller.
@Module({
controllers: [OrdersController],
providers: [OrdersService],
})
export class AppModule {} TypeScript (app.ts) · three-ways/nest/src/app.ts
import { StandardSchemaValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module.js';
// Builds the application without starting it, so main.ts and the comparison can both use it.
export async function createApp() {
const app = await NestFactory.create(AppModule, { logger: false });
app.useGlobalPipes(new StandardSchemaValidationPipe());
return app;
} TypeScript (main.ts) · three-ways/nest/src/main.ts
import { createApp } from './app.js';
const app = await createApp();
await app.listen(process.env.PORT ?? 3000); The session below compiles the Nest version, starts all three on ports the system picks, sends each the same three bodies, and counts the files and lines of each version:
import { session } from '../session.mjs';
// Compiles the Nest version, sends the same three requests to all three, then counts their files and lines.
session(`
npx tsc -p nest/tsconfig.json
node compare.mjs
node sizes.mjs
`); Output
$ npx tsc -p nest/tsconfig.json
$ node compare.mjs
POST /orders {"kitchenId":"pune-07","meals":3}
express 201 {"id":1,"kitchenId":"pune-07","meals":3}
fastify 201 {"id":1,"kitchenId":"pune-07","meals":3}
nest 201 {"id":1,"kitchenId":"pune-07","meals":3}
POST /orders {"kitchenId":"pune-07","meals":11}
express 400 {"message":"kitchenId must be text and meals a whole number from 1 to 10"}
fastify 400 {"statusCode":400,"code":"FST_ERR_VALIDATION","error":"Bad Request","message":"body/meals must be <= 10"}
nest 400 {"message":["meals: Too big: expected number to be <=10"],"error":"Bad Request","statusCode":400}
POST /orders {"kitchenId":"pune-07","meals":"3"}
express 400 {"message":"kitchenId must be text and meals a whole number from 1 to 10"}
fastify 201 {"id":2,"kitchenId":"pune-07","meals":3}
nest 400 {"message":["meals: Invalid input: expected number, received string"],"error":"Bad Request","statusCode":400}
$ node sizes.mjs
express 1 file 13 lines orders.mjs
fastify 1 file 16 lines orders.mjs
nest 6 files 45 lines app.module.ts, app.ts, create-order.schema.ts, main.ts, orders.controller.ts, orders.service.ts
Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node run.mjs
The first body passes everywhere and the second fails everywhere, each with its own error shape. The third is the
surprise: "meals": "3" is text, yet Fastify answered 201 and stored the number 3, because Fastify configures its
JSON Schema validator with coerceTypes: 'array', which turns "3" into 3 before checking it. The hand-written
check and Zod’s z.number() reject it with 400.
| Version | Files | Lines of code | Where the check lives | "meals": "3" |
|---|---|---|---|---|
| Express 5 | 1 | 13 | your handler | 400 |
| Fastify 5 | 1 | 16 | a JSON Schema on the route | 201, as the number 3 |
| NestJS 12 | 6 | 45 | a Zod schema, run by a pipe | 400 |
Taking “validated” to mean the same thing everywhere
Three frameworks, three answers to one request. Before you move an API from one framework to another, send it the same edge cases on both, as this session does, and compare status codes and bodies: a coercion or a missing check changes what your database receives without any error.
Checkpoint
Check: One endpoint in three frameworks
What the extra files buy, and what they cost
Nest’s six files are not six times the work, because each one has a single job and a fixed place. A new person on
the team finds the rule for orders in OrdersService, the HTTP mapping in OrdersController and the wiring in
OrdersModule, in every feature of every Nest app. The same structure carries over to GraphQL resolvers, WebSocket
gateways and message-broker handlers, which use the same modules, providers and pipes. And because the controller
receives its service through the constructor, a test can hand it a fake service without starting a server.
The costs are real, too:
| Nest gives you | Nest costs you |
|---|---|
| One layout for every feature: modules, controllers, providers | More files per feature: 6 against 1 for this endpoint |
| Injection, so classes can be tested with fakes | Concepts to learn first: modules, providers, scopes, pipes |
| Validation, guards and error handling in fixed places | A compile step, because decorators are not plain JavaScript |
| One programming model for HTTP, GraphQL, sockets and brokers | Start-up work: Nest reads every module before the first request |
The compile step deserves a word now, because it surprises people who run TypeScript directly with Node.js. Express
and Fastify code is plain JavaScript, so node orders.mjs runs it. Nest code needs a TypeScript compiler to turn its
decorators into function calls and to record the types of constructor parameters, which is why the session above ran
npx tsc first. The last lesson of this module shows exactly what that compiler writes.
Key idea
Nest trades more files and a build step for a fixed shape, injection and a shared pipeline. The trade pays off as the team, the codebase and the number of transports grow.
Checkpoint
Check: What the extra files buy and cost
Choosing Nest, Express, Fastify or Hono for your case
The decision mostly comes down to three questions about your service. Answer them in the table below; it marks the frameworks your answers rule out or warn about, and the columns that decide:
Answer the questions above: the table then marks what fits your case and says why the rest does not.
| Framework | Structure for a team | Beyond HTTP | Where it runs | Checking input | Build and size |
|---|---|---|---|---|---|
| NestJS 12 | Good modules, injection and one shape for every feature | Good one model for HTTP, GraphQL, WebSockets and message brokers | Mixed a Node.js server, through Express or Fastify | Good pipes check a body against a schema before your method runs | Poor a compile step, and 6 files with 45 lines for the lesson's endpoint |
| Express 5 | Mixed no prescribed layout: each team chooses its own folders and wiring | Poor HTTP only; sockets and queues are separate libraries you wire up | Mixed a Node.js server | Mixed by hand or with a library you add; nothing is checked by default | Good 1 file with 13 lines for the same endpoint, and no build |
| Fastify 5 | Mixed plugins with their own scope, but no prescribed layout | Mixed HTTP first; WebSockets and the rest through plugins | Mixed a Node.js server | Good a JSON Schema per route, checked before the handler, with type coercion on | Good 1 file with 16 lines for the same endpoint, and no build |
| Hono | Mixed a small core with routers and middleware; the layout is yours | Poor HTTP handlers on the web-standard Request and Response | Good the same code on Node.js, Deno, Bun, Fastly Compute and AWS Lambda | Mixed validator middleware that you add to each route | Good one small package with no dependencies, and no decorator build |
How the answers decide
- “Where must it run?” An edge or web-standard runtime: rules out NestJS 12, Express 5 and Fastify 5. Express and Fastify, and so Nest on top of them, need a Node.js HTTP server. Hono's handlers use the Request and Response that edge runtimes provide.
- “How many people will work on this service over its life?” One or two people and “Does it need more than an HTTP API: GraphQL, WebSockets or a message broker?” No, an HTTP API: adds a caveat to NestJS 12. For one or two people and one HTTP API, Nest's modules, injection and build step can cost more than they save. Express or Fastify is less to learn.
- “How many people will work on this service over its life?” A team, or several teams: adds a caveat to Express 5, Fastify 5 and Hono. Without a prescribed layout, each team settles its own folders, wiring and error format. Nest gives every feature the same shape.
- “Does it need more than an HTTP API: GraphQL, WebSockets or a message broker?” Yes, or soon: adds a caveat to Express 5, Fastify 5 and Hono. GraphQL, sockets and brokers each bring their own library and style. Nest runs them with the same modules, providers and pipes as HTTP.
In short:
- NestJS fits a team that will grow, a codebase that will live for years, or a service that will speak more than HTTP. It runs on a Node.js server through Express or Fastify.
- Express fits a small HTTP service or a quick prototype, and code that already depends on Express middleware. Express 5 is the library Nest itself uses by default.
- Fastify fits an HTTP service that wants checks declared per route and fast JSON handling with little code. Remember that it coerces types unless you configure its validator otherwise.
- Hono fits code that must run on an edge or web-standard runtime, such as Fastly Compute, Deno or Bun.
Its handlers take the web-standard
Requestand return aResponse.
Checkpoint
Check: Choosing a framework for a case
Read the release line before you start
npm keeps a few named pointers for each package, its dist-tag: A name on the npm registry, such as latest or legacy, that points at one version of a package. npm install name@tag installs that version, and maintainers move tags by hand.. npm install @nestjs/core installs the version latest points at, and npm install @nestjs/core@legacy the one legacy points
at. npm publish moves latest to each new release unless the maintainer names another tag, and every other tag
moves only when a maintainer moves it, so a tag can be stale. This program reads a saved copy of the registry’s
metadata for @nestjs/core and prints where each tag points and what each major line declares:
// Reads a saved copy of npm's metadata for @nestjs/core (registry-snapshot.json) and prints where each dist-tag
// points, then, for each major line, its stable releases, the newest one and what their engines fields declare.
import { readFileSync } from 'node:fs';
const meta = JSON.parse(readFileSync('registry-snapshot.json', 'utf8'));
function parse(version) {
const [, major, minor, patch, pre = ''] = /^(\d+)\.(\d+)\.(\d+)(-.+)?$/.exec(version);
return { major: Number(major), minor: Number(minor), patch: Number(patch), pre };
}
// Older first; a pre-release comes before the release it leads to (12.0.0-alpha.7 < 12.0.0).
function compare(a, b) {
const x = parse(a);
const y = parse(b);
return x.major - y.major || x.minor - y.minor || x.patch - y.patch || (x.pre === y.pre ? 0 : x.pre === '' ? 1 : y.pre === '' ? -1 : x.pre < y.pre ? -1 : 1);
}
const stable = Object.keys(meta.versions).filter((v) => parse(v).pre === '').sort(compare);
const newestOf = (major) => stable.filter((v) => parse(v).major === major).at(-1);
const latest = meta['dist-tags'].latest;
console.log('dist-tags of @nestjs/core, newest version first:');
const tags = Object.entries(meta['dist-tags']).sort(([, a], [, b]) => compare(b, a));
for (const [tag, version] of tags) {
const { major, pre } = parse(version);
let note = '';
if (pre) note = compare(version, latest) < 0 ? 'a pre-release, older than latest' : 'a pre-release';
else if (newestOf(major) !== version) note = `${newestOf(major)} is newer on the same line`;
console.log(` ${tag.padEnd(7)} ${version.padEnd(15)} ${note}`.trimEnd());
}
// Every stable release of a line is read, so "all declare" is checked, not assumed from the newest one.
console.log('Stable releases of each major line, and what their engines fields declare:');
for (const major of [12, 11, 10]) {
const line = stable.filter((v) => parse(v).major === major);
const declared = [...new Set(line.map((v) => meta.versions[v].engines?.node ?? null))];
let engines;
if (declared.length > 1) engines = `mixed: ${declared.map((d) => d ?? 'none').join(', ')}`;
else engines = declared[0] === null ? 'none declares engines' : `all declare node ${declared[0]}`;
console.log(` ${major}.x ${String(line.length).padStart(2)} releases, newest ${newestOf(major).padEnd(8)} ${engines}`);
} Output
dist-tags of @nestjs/core, newest version first: latest 12.1.3 next 12.0.0-alpha.7 a pre-release, older than latest legacy 11.2.7 old 10.4.19 10.4.22 is newer on the same line beta 9.3.0-beta.3 a pre-release, older than latest rc 6.0.0-rc.1 a pre-release, older than latest Stable releases of each major line, and what their engines fields declare: 12.x 9 releases, newest 12.1.3 all declare node >= 20 11.x 60 releases, newest 11.2.7 all declare node >= 20 10.x 55 releases, newest 10.4.22 none declares engines
Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node release_line.mjs
Read it from the top. latest is 12.1.3. next, beta and rc are old pre-releases that nobody moved forward, so
npm install @nestjs/core@next would install an alpha of 12.0 that is older than every 12.x release. old points at
10.4.19 although 10.4.22 is newer on the same line; npm install @nestjs/core@10 asks for the newest 10.x instead.
The engines field is the other thing to read: every 11.x and 12.x release declares node >= 20, and no 10.x release
declares anything.
Walk through the three lines, oldest first, to see what each tag stands for:
- 10.x
The line npm calls old old tag
The old tag points at 10.4.19, although 10.4.20, 10.4.21 and 10.4.22 were published later on the same line. No 10.x package declares the Node.js versions it needs.
- 11.x
The line npm calls legacy legacy tag
11.2.7 carries the legacy tag, the newest 11.x for apps that have not moved to 12 yet. Every 11.x release declares node >= 20 in its engines field.
- 12.0
ES modules and new defaults major
The Nest packages ship as ES modules, and nest new asks for an ESM project with Vitest or a CommonJS one with Jest. Route conflict checks, Standard Schema validation and machine-readable error codes arrive.
- 12.1
Cookies, CSRF checks and security headers minor
Cookies, CSRF protection and security headers become built-in features that work on both Express and Fastify.
- 12.1.3
The release npm calls latest latest tag
The latest tag points at 12.1.3, a patch release. The examples of this track run on 12.1.2, one patch earlier.
The timeline has 5 events, from 10.x to 12.1.3.
The engines field is a floor, not the whole story. The migration guide asks for Node.js 20.19 or later, or 22.12 or later on the 22 line, to run a Nest 12 app, and the CLI’s code generators ask for 22.22.3, 24.15 or 26. The next lesson starts a project with Node.js 24.21.0, which meets both.
How widely Nest is used
In Stack Overflow’s 2025 developer survey, 6.7 % of all respondents said they had done extensive development work in NestJS over the past year, against 19.9 % for Express and 2.9 % for Fastify. A survey measures who answered it, not the whole industry, so treat the numbers as a rough guide to how many people you can ask for help.
Checkpoint
Check: Reading dist-tags and engines
Practise: pick the framework
Each case below names a team, a workload and one constraint. Pick the framework that fits, then compare your reason with the explanation under the answer:
Practice that adapts to you
Practise this lesson
10 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.
-
Fresher to mid · Warm-up
What does NestJS add on top of Express, and what does Express still do in a Nest app?
Express still does the HTTP work: on Nest's default adapter it parses each request and writes each response, which is why a Nest response says X-Powered-By: Express. Nest registers one Express route per handler at start-up and adds four things around them. Modules group controllers and providers into features. A dependency-injection container builds each provider once and passes it to the classes whose constructors ask for it. Decorators such as @Controller() and @Post() declare the routes. And a pipeline of middleware, guards, interceptors, pipes and exception filters runs around every handler, so checks and errors live in fixed places. The cost is a compile step and more files: in this lesson's comparison, 45 lines in six files against 13 lines in one Express file.
-
Mid to senior
Your team of eight is starting an order service. It needs a REST API now, will consume a message broker next quarter, and must be maintained for years. Would you build it with NestJS or plain Express 5? Defend the choice, and say what it will cost the team.
-
Mid to senior
The body {"kitchenId":"pune-07","meals":"3"} got 201 from a Fastify service and 400 from its NestJS replacement, which validates with Zod. Explain what happened, and how you would move existing clients to the new service without breaking them.
Key takeaways
- Nest registers its routes on Express 5 by default, or on Fastify 5; that library parses requests and writes the responses, and Nest adds modules, dependency injection, route decorators and a pipeline around every handler.
- The same endpoint took 13 lines in Express, 16 in Fastify and 45 lines in six files in Nest, and Nest’s version needs a compile step.
- The same body can get different answers: Fastify coerced
"3"to 3 and answered 201, while the hand-written check and Zod’s schema answered 400. - Nest pays off for teams, long-lived code and services that speak more than HTTP; Express and Fastify suit small HTTP services, and Hono suits edge and web-standard runtimes.
- npm’s dist-tags are pointers:
npm publishmoveslatest, and every other tag moves only when a maintainer moves it, so check whatlatest,legacyandoldpoint at, and the engines field, before you install.
Terms in this lesson
- controller
- A class marked with @Controller() whose methods handle requests. Each method with a route decorator such as @Get() or @Post() becomes one route of the app.
- dependency injection
- A way of building objects in which a class asks for what it needs in its constructor and a container creates those objects and passes them in, instead of the class creating them itself.
- dist-tag
- A name on the npm registry, such as latest or legacy, that points at one version of a package. npm install name@tag installs that version, and maintainers move tags by hand.
- HTTP adapter
- The part of Nest that wraps an HTTP library, Express or Fastify. Nest registers its routes on the library through it, and the library parses requests and writes responses. @nestjs/platform-express is the default.
- module
- A class marked with @Module() that lists the controllers and providers that belong together, the modules it imports and the providers it shares with them. Every Nest app starts from one root module.
- pipe
- A class Nest runs on a handler's argument before the handler, to check it or turn it into another type. A pipe that throws stops the request with an error response.
- provider
- Anything Nest's container can create and inject, most often a class marked with @Injectable() such as a service. By default Nest creates one instance of each provider and shares it.
- Standard Schema
- A common interface that validation libraries such as Zod, Valibot and ArkType implement, so that a framework can check data against a schema from any of them. Nest 12's StandardSchemaValidationPipe uses it.
References
- NestJS documentation: Introduction (NestJS)
- NestJS documentation: First steps (platforms) (NestJS)
- NestJS documentation: Controllers (request payloads and schemas) (NestJS)
- NestJS documentation: Migration guide (Node.js requirements) (NestJS)
- NestJS v12.0.0 release notes (NestJS (GitHub))
- NestJS v12.1.0 release notes (NestJS (GitHub))
- NestJS documentation: CSRF protection (NestJS)
- NestJS documentation: Helmet (built-in security headers) (NestJS)
- @nestjs/core on npm (npm)
- npm documentation: npm dist-tag (npm)
- npm documentation: package.json, engines (npm)
- Express 5 API reference: express.json() (OpenJS Foundation (Express))
- Fastify documentation: Validation and Serialization (OpenJS Foundation (Fastify))
- Hono documentation (Hono)
- Standard Schema (Standard Schema)
- Stack Overflow Developer Survey 2025: Technology (Stack Overflow)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress