Concepts
1. NestJS Architecture
| Element | Role | Typical File |
|---|---|---|
| Module | Logical grouping of related components; the building block of the application graph. | src/app.module.ts |
| Controller | Handles incoming HTTP requests, delegates work to providers. | src/app.controller.ts |
| Provider (Service) | Contains business logic, can be injected anywhere via Nest’s DI container. | src/app.service.ts |
| Decorator | Adds metadata to classes, methods, or parameters (@Module, @Controller, @Get, @Injectable, …). | – |
| Dependency Injection (DI) | Nest resolves provider instances automatically based on the metadata supplied by decorators. | – |
2. Request Lifecycle (simplified)
- Router matches the request path to a controller method.
- Guards (optional) run before the handler.
- Interceptors (optional) can transform input/output.
- Pipe validation/transformation runs on method arguments.
- Controller method executes, typically calling a service.
- Response is sent back to the client.
3. Nest CLI
- Provides commands for scaffolding, building, testing, and running projects.
- All generated code follows Nest’s recommended folder structure and naming conventions.
Key commands (run from a terminal inside the project root):
| Command | Purpose |
|---|---|
nest new <project> | Create a new Nest application (prompts for package manager). |
nest generate <schematic> <name> or nest g <s> <n> | Scaffold modules, controllers, services, etc. (module, controller, service, gateway, filter, …). |
npm run start:dev | Run the app with hot‑reloading (uses ts-node-dev). |
npm run build | Compile TypeScript to JavaScript (dist/ folder). |
npm run test | Execute unit tests (Jest). |
4. Project Scaffolding Overview
When nest new finishes, the generated tree looks like:
src/
├─ app.controller.ts
├─ app.module.ts
├─ app.service.ts
└─ main.ts
test/
└─ app.e2e-spec.ts
package.json
tsconfig.json
nest-cli.json
main.tsboots the Nest application (NestFactory.create(AppModule)).AppModuleimports other modules and registers providers.
Examples
1. Install the Nest CLI and create a project
# Install globally (requires Node.js ≥ 14)
npm i -g @nestjs/cli
# Scaffold a new project called "backend-demo"
nest new backend-demo
During creation, choose npm (or yarn/pnpm) as the package manager. The CLI runs npm install automatically.
2. Explore the generated files
cd backend-demo
tree src
Output:
src
├── app.controller.ts
├── app.module.ts
├── app.service.ts
└── main.ts
Open src/app.controller.ts:
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return appService.getHello();
}
}
3. Run the application
npm run start:dev
The console prints:
[Nest] 12345 - 2025-12-20 10:00:00 LOG Nest application successfully started
Open a browser or use curl:
curl http://localhost:3000
# => "Hello World!"
4. Add a new feature module
Create a users module with a controller and service:
nest g module users
nest g controller users
nest g service users
Generated files:
src/users/users.module.ts
src/users/users.controller.ts
src/users/users.service.ts
users.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class UsersService {
private readonly users = [{ id: 1, name: 'Alice' }];
findAll() {
return this.users;
}
findOne(id: number) {
return this.users.find(u => u.id === id);
}
}
users.controller.ts
import { Controller, Get, Param } from '@nestjs/common';
import { UsersService } from './users.service';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Get()
findAll() {
return this.usersService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.usersService.findOne(+id);
}
}
users.module.ts
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
@Module({
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}
Register the module in app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UsersModule } from './users/users.module';
@Module({
imports: [UsersModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Restart the dev server (npm run start:dev). Test the new endpoints:
curl http://localhost:3000/users
# => [{"id":1,"name":"Alice"}]
curl http://localhost:3000/users/1
# => {"id":1,"name":"Alice"}
5. Verify TypeScript compilation
npm run build
# Output placed in ./dist
node dist/main.js
The same routes work on port 3000.
Key notes
- Modules are the unit of composition – every feature (e.g., users, auth) should have its own module.
- Controllers stay thin – they only map HTTP verbs to service calls; business logic belongs in providers.
- Providers are singletons by default – Nest creates one instance per module unless
@Scope()changes it. - CLI naming – use kebab‑case for file names (
users.service.ts) and PascalCase for class names (UsersService). - Environment configuration – keep secrets out of source; later chapters will introduce
@nestjs/config. - Hot‑reload (
start:dev) usests-node-dev, which watches.tsfiles. For production, always run the compiled JavaScript (node dist/main.js). - Testing scaffold – the CLI also creates a basic e2e test (
test/app.e2e-spec.ts). Keep it as a baseline for future test additions.