Official Documentation
Untuk dokumentasi lengkap, kunjungi:
Project Layout
-
project
-
src
-
main.ts # Entry point
-
app.module.ts # Root module
-
modules
-
users
-
users.module.ts
-
users.controller.ts
-
users.service.ts
-
users.repository.ts
-
dto
-
create-user.dto.ts
-
update-user.dto.ts
-
-
-
-
common/ # Filters, interceptors, pipes, guards
-
config/ # App config
-
-
test
-
package.json
-
nest-cli.json
-
Module Pattern
// users.module.ts
@Module({
controllers: [UsersController],
providers: [UsersService, UsersRepository],
exports: [UsersService],
})
export class UsersModule {}
Controller
@Controller("users")
export class UsersController {
constructor(private readonly service: UsersService) {}
@Get()
async findAll() {
return this.service.findAll();
}
@Post()
async create(@Body() dto: CreateUserDto) {
return this.service.create(dto);
}
}
DTO Validation
Gunakan class-validator + class-transformer:
import { IsString, IsEmail, MinLength } from "class-validator";
export class CreateUserDto {
@IsString()
@MinLength(2)
name: string;
@IsEmail()
email: string;
}
Database
Gunakan Prisma atau TypeORM:
// Prisma schema
model User {
id Int @id @default(autoincrement())
name String
email String @unique
}
Testing
# Unit test
pnpm test
# E2E
pnpm test:e2e
// Controller unit test
describe("UsersController", () => {
let controller: UsersController;
let service: UsersService;
beforeEach(async () => {
const module = await Test.createTestingModule({
controllers: [UsersController],
providers: [{ provide: UsersService, useValue: mockService }],
}).compile();
controller = module.get(UsersController);
});
it("should return all users", async () => {
expect(await controller.findAll()).toEqual(mockUsers);
});
});
Configuration Management
Gunakan @nestjs/config untuk mengatur environment variables dengan validasi:
import { ConfigModule } from "@nestjs/config";
import * as Joi from "joi";
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
validationSchema: Joi.object({
DATABASE_URL: Joi.string().required(),
PORT: Joi.number().default(3000),
}),
}),
],
})
export class AppModule {}
Exception Filters
Gunakan global exception filter untuk menstandarkan struktur error response:
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
} from "@nestjs/common";
import { Response } from "express";
@Catch(HttpException)
export class GlobalExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const status = exception.getStatus();
const exceptionResponse = exception.getResponse();
response.status(status).json({
success: false,
error:
typeof exceptionResponse === "string"
? { message: exceptionResponse }
: exceptionResponse,
});
}
}
Daftarkan di main.ts: app.useGlobalFilters(new GlobalExceptionFilter()).
Interceptors
Gunakan interceptor untuk mapping response menjadi format amplop (envelope) standard:
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
} from "@nestjs/common";
import { Observable } from "rxjs";
import { map } from "rxjs/operators";
@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, any> {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
map((data) => ({
success: true,
data,
})),
);
}
}
Daftarkan di main.ts: app.useGlobalInterceptors(new TransformInterceptor()).
Guards (Auth & RBAC)
Gunakan Guards untuk melindungi route:
import {
Injectable,
CanActivate,
ExecutionContext,
UnauthorizedException,
} from "@nestjs/common";
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
const token = request.headers.authorization;
if (!token) throw new UnauthorizedException("Token required");
return true;
}
}
// Di Controller:
// @UseGuards(AuthGuard)
Deployment
Build dan run:
pnpm build
node dist/main.js
Atau dengan Docker:
FROM node:22-alpine AS builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack prepare pnpm@latest --activate && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
FROM node:22-alpine AS runner
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
CMD ["node", "dist/main.js"]