@nestjs/core review
`@nestjs/core` 11.2.3 contains Nest's module scanner, dependency-injection container, application factory, and request pipeline. It connects controllers and providers to an Express or Fastify adapter; installing core alone does not create a usable HTTP server. The 11.2 line adds HTTP `QUERY` handling, better singleton sharing and discovery for lazy modules, and an abort signal for server-sent event producers. Patch 11.2.1 repaired an early SSE return, 11.2.2 fixed startup failures around instrumentation and proxy providers, and 11.2.3 fixes circular durable providers. The package remains CommonJS, bundles TypeScript declarations, and requires Node 20 or newer.
Our `@nestjs/core` 11.2.1 install took 8.8 seconds, left 27 packages and 17 MB, passed npm audit, and failed a browser build because this is Node-only infrastructure. Adopt the current 11.2.3 package for a backend that needs Nest's container and module rules; a small route set is cheaper to own in Fastify, Express, or Hono.
We installed it
| Install | ✓ · 8.8s | 27 packages on disk · 17 MB |
| Import | ✓ | ESM import works · require() works · CommonJS package |
| Browser | n/a | could not be bundled for the browser (Node-only code, most likely) |
| Types | ✓ | TypeScript types bundled |
| Known vulns | 0 | 0 critical · 0 high · 0 moderate · 0 low (npm audit) |
Answers from our run
Does @nestjs/core install cleanly?
Yes. In a fresh container with an empty cache, npm install @nestjs/core finished in 9 seconds, leaving 27 packages and 17 MB on disk. npm audit reported no known vulnerabilities.
Can @nestjs/core run in a browser?
Not directly: esbuild could not bundle it for the browser in our run, which normally means it depends on Node built-ins. Use it on the server, or find a browser-targeted alternative.
Does @nestjs/core work with both ESM and CommonJS?
Yes. Both import '@nestjs/core' and require('@nestjs/core') worked in Node 22 in our run. The package is published as CommonJS.
Does @nestjs/core include TypeScript types?
Yes, type declarations ship inside the package, so no @types install is needed.
@nestjs/core or fastify: which should you use?
fastify: Pick it for schema-aware HTTP handling when the team wants to choose its own architecture. Our @nestjs/core 11.2.1 install took 8.8 seconds, left 27 packages and 17 MB, passed npm audit, and failed a browser build because this is Node-only infrastructure.
When should you not use @nestjs/core?
A service has only a handful of routes. Modules, providers, DTO classes, decorators, and bootstrap code can cost more than direct Fastify or Express handlers.
Use it if
- Several teams need one enforced structure for modules, providers, controllers, and the request lifecycle.
- The backend will combine official Nest integrations for GraphQL, WebSockets, jobs, scheduling, or message transports.
- The codebase already accepts decorators, constructor injection, and class-centered TypeScript design.
- Tests benefit from replacing a provider in a compiled module rather than monkeypatching imports.
- A service has only a handful of routes. Modules, providers, DTO classes, decorators, and bootstrap code can cost more than direct Fastify or Express handlers.
- Decorator metadata is forbidden by the build. Normal constructor injection expects TypeScript decorator settings plus `reflect-metadata`.
- The target is a browser or edge isolate. Our esbuild browser build failed, and core expects a Node server environment.
- Developers want to work directly with adapter request and response objects. Debugging through Nest plus Express or Fastify requires understanding both layers.
- You expect one package to be enough. Core has 6 peers, including required `@nestjs/common`, `rxjs`, and `reflect-metadata`, and HTTP needs a platform adapter.
- Node 18 or older must remain supported. Nest 11 requires Node 20 and follows Express 5 routing behavior on the default adapter.
Setup reality
We installed @nestjs/core 11.2.1 in a fresh Node 22 Bookworm container in 8.8 seconds. The result was 27 packages using 17 MB, with zero findings from npm audit. Core itself declared 5 direct and 6 peer dependencies and was 1,916 KB unpacked. Bundled types were present. Both require() and ESM import worked even though the package is CommonJS and has no exports map. The browser build failed in esbuild, which is the expected result for this Node-only runtime.
Three peers are basic requirements: @nestjs/common, rxjs, and reflect-metadata. The peer list also includes platform-express, websockets, and microservices as optional. An HTTP service must install either @nestjs/platform-express or @nestjs/platform-fastify on the same Nest major. A manual TypeScript setup also needs decorator metadata enabled and loaded before decorated application classes. NestFactory.create() cannot start until a root module has been defined.
Core does not include request validation. DTO validation usually adds class-validator, class-transformer, and a ValidationPipe; configuration, OpenAPI, GraphQL, queues, and persistence are separate choices too. Module boundaries control provider visibility, so a provider must be exported by its declaring module and that module imported by its consumer. Register injected global guards, pipes, filters, and interceptors with the APP_* tokens. Instances passed directly with new sit outside ordinary container construction.
Express and Fastify adapters expose different middleware and request objects. A Fastify server in a container normally listens on 0.0.0.0, not only loopback. Enable shutdown hooks if providers must release resources on SIGTERM. The 11.2 series changed lazy-module and SSE behavior, then 11.2.2 and 11.2.3 repaired startup and circular durable-provider failures. We measured 11.2.1, so teams using those features should take the current 11.2.3 patch and rerun their bootstrap tests.
Patterns
Start an HTTP application bootstrap-application
import 'reflect-metadata';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function start() {
const app = await NestFactory.create(AppModule);
app.setGlobalPrefix('api');
await app.listen(Number(process.env.PORT ?? 3000));
}
void start();A manual project must import `reflect-metadata` before its decorated classes load. CLI-generated projects already include the matching compiler configuration.
Expose a provider from a feature module declare-module
import { Module } from '@nestjs/common';
import { OrdersController } from './orders.controller';
import { OrdersService } from './orders.service';
@Module({
controllers: [OrdersController],
providers: [OrdersService],
exports: [OrdersService],
})
export class OrdersModule {}`OrdersService` stays inside this module until `exports` exposes it. Another module must import `OrdersModule` before injection can resolve the service.
Map requests to controller methods create-controller
import { Body, Controller, Get, Param, Post } from '@nestjs/common';
@Controller('orders')
export class OrdersController {
constructor(private readonly orders: OrdersService) {}
@Get(':id')
find(@Param('id') id: string) {
return this.orders.find(id);
}
@Post()
create(@Body() input: CreateOrderDto) {
return this.orders.create(input);
}
}A returned value passes through Nest serialization and interceptors. Writing through the adapter response object can switch the handler to platform-specific response handling.
Create a service for constructor injection inject-provider
import { Injectable, NotFoundException } from '@nestjs/common';
@Injectable()
export class OrdersService {
constructor(private readonly store: OrderStore) {}
async find(id: string) {
const order = await this.store.find(id);
if (!order) throw new NotFoundException(`Order ${id} was not found`);
return order;
}
}The module graph must make both provider tokens visible. `NotFoundException` carries HTTP 404, whereas an ordinary `Error` becomes an internal-server response.
Strip unknown fields and validate DTOs validate-request-body
import { ValidationPipe } from '@nestjs/common';
import { IsInt, IsString, Min } from 'class-validator';
export class CreateOrderDto {
@IsString() customerId!: string;
@IsInt() @Min(1) quantity!: number;
}
app.useGlobalPipes(new ValidationPipe({
whitelist: true,
transform: true,
}));Core does not install `class-validator` or `class-transformer`. `whitelist` strips unknown fields; `forbidNonWhitelisted` turns them into a request error.
Reject requests through a guard protect-routes
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
@Injectable()
export class ApiKeyGuard implements CanActivate {
canActivate(context: ExecutionContext) {
const request = context.switchToHttp().getRequest();
if (request.headers['x-api-key'] !== process.env.API_KEY) {
throw new UnauthorizedException();
}
return true;
}
}A guard that returns `false` yields HTTP 403. Throw `UnauthorizedException` when failed authentication must produce HTTP 401 instead.
Register a global guard with dependency injection register-global-guard
import { APP_GUARD } from '@nestjs/core';
@Module({
providers: [
ApiKeyGuard,
{ provide: APP_GUARD, useExisting: ApiKeyGuard },
],
})
export class SecurityModule {}An instance passed to `useGlobalGuards()` was constructed outside the container. `APP_GUARD` lets Nest resolve its constructor dependencies normally.
Run Nest on the Fastify adapter switch-to-fastify
import { NestFactory } from '@nestjs/core';
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter(),
);
await app.listen({ port: 3000, host: '0.0.0.0' });Keep `@nestjs/platform-fastify` on core's major version. Middleware using Express-only request or response methods cannot move across unchanged.
Replace a provider in a unit test test-with-container
const moduleRef = await Test.createTestingModule({
controllers: [OrdersController],
providers: [OrdersService, OrderStore],
})
.overrideProvider(OrderStore)
.useValue({ find: jest.fn().mockResolvedValue({ id: 'o1' }) })
.compile();
const controller = moduleRef.get(OrdersController);
await expect(controller.find('o1')).resolves.toEqual({ id: 'o1' });`Test` comes from the separate `@nestjs/testing` package. A small testing module avoids booting the HTTP adapter and unrelated providers.
Close resources when the process receives SIGTERM handle-shutdown
@Injectable()
export class DatabaseService implements OnApplicationShutdown {
constructor(private readonly pool: Pool) {}
async onApplicationShutdown() {
await this.pool.end();
}
}
app.enableShutdownHooks();`enableShutdownHooks()` connects process signals to lifecycle methods. Several Nest applications in one process each add listeners and can trigger listener-limit warnings.
Configure a reusable module asynchronously create-dynamic-module
@Module({})
export class StorageModule {
static forRootAsync(): DynamicModule {
return {
module: StorageModule,
providers: [{
provide: 'STORAGE_OPTIONS',
inject: [ConfigService],
useFactory: (config: ConfigService) => ({ bucket: config.getOrThrow('BUCKET') }),
}],
exports: ['STORAGE_OPTIONS'],
};
}
}Dynamic configuration does not bypass module visibility. Export the token and import the returned module wherever consumers need it.
Return a server-sent event stream send-server-events
import { Controller, MessageEvent, Sse } from '@nestjs/common';
import { interval, map, Observable } from 'rxjs';
@Controller('jobs')
export class JobsController {
@Sse('events')
events(): Observable<MessageEvent> {
return interval(1000).pipe(
map((count) => ({ data: { count } })),
);
}
}The observable emits `MessageEvent` shapes. The 11.2 line changed disconnect cancellation, and 11.2.1 repaired an early-return path; current 11.2.3 includes that fix.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| fastify | npm | Pick it for schema-aware HTTP handling when the team wants to choose its own architecture. |
| express | npm | Pick it for a small or middleware-heavy service that does not need a dependency-injection container. |
| hono | npm | Pick it when the same compact routing API must run on edge platforms as well as Node. |
More web backend guides
urllib3 · requests · ws · anyio · undici · httpx · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

