Hello, World!
A running NestJS application in three commands, and what each generated file is actually for.
NestJS is a Node.js framework that gives a back end the thing Express deliberately does not: structure. Modules, dependency injection, decorators, and a strong convention for where everything goes.
That structure is why it is worth learning in 2026. A codebase where every piece has an obvious home is one you can review quickly — and reviewing is now most of the job.
Create the project#
npm i -g @nestjs/cli
nest new hello-nest
cd hello-nest && npm run start:devOpen http://localhost:3000. You will see Hello World!.
What was generated#
src/
main.ts entry point. creates the app, listens on a port.
app.module.ts the root module. wires everything together.
app.controller.ts handles HTTP requests. no business logic.
app.service.ts the logic. injected into the controller.
app.controller.spec.tsThat four-file split is the whole mental model, and it repeats at every level of a Nest application.
The entry point#
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();NestFactory.create walks the module graph, constructs every provider, resolves every dependency, and hands you an application.
The controller#
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}Two things are happening that are easy to miss:
@Controller()with no argument means this handles the root path.@Controller('users')would prefix every route in the class with/users.private readonly appService: AppServicein the constructor is the whole dependency injection system. You never writenew AppService(). Nest reads the type, finds the provider, and passes it in.
The service#
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}@Injectable() marks the class as something Nest can construct and inject. Business logic lives here — not in the controller.
The module#
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}A module is a declaration of what belongs together. controllers handle requests; providers can be injected; exports makes a provider available to other modules that import this one.
Add a route#
@Get('health')
health(): { status: string; at: string } {
return { status: 'ok', at: new Date().toISOString() };
}http://localhost:3000/health now returns JSON. Nest serialises objects automatically and sets the content type.
Common questions#
Is NestJS just Angular for the back end?#
The decorator syntax and the dependency injection are deliberately Angular-like, and that is where the resemblance ends. Nest runs on Express or Fastify underneath and is a server framework throughout.
Do I need to know Express?#
No, but it helps when you need to drop down a level. Nest exposes the underlying request and response objects when you ask for them, and most Express middleware works unchanged.
Express or Fastify?#
Express is the default and has the larger middleware ecosystem. Fastify is faster and the adapter is a one-line change. Start on Express; switch if benchmarks ever tell you to.
Get the NestJS agent pack
A battle-tested AGENTS.md, the review checklist, and the failure-mode cheat sheet for NestJS. One email, then occasional updates when the tooling shifts. No course pitch.
AGENTS.md now — no email needed.