跪拜 Guibai
← All articles
Backend

A Complete NestJS CRUD Module, Layer by Layer

By 东风破_ ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

NestJS's opinionated three-layer architecture is the default for enterprise Node.js backends, and this pattern — Controller, Service, Module — repeats identically whether the module handles todos, auth, or payments. Understanding the exact wiring and data flow removes the guesswork when scaling from a toy to a production service.

Summary

Starting from a simple in-memory data model, a complete Todos API comes together across four distinct layers. The Service layer houses all business logic and uses NestJS's built-in `NotFoundException` to produce standard HTTP 404 responses. The Controller layer maps HTTP verbs to routes, extracts path parameters and request bodies with dedicated decorators, and delegates work to the injected Service. The Module layer registers both Controller and Service, then plugs into the root AppModule. The full request lifecycle — from route matching and parameter parsing through dependency injection and exception filtering — is traced end-to-end for a `GET /todos/1` call. A Jest unit-test scaffold and curl verification commands round out the implementation.

Takeaways
An in-memory array with an auto-incrementing ID field is enough to model data while learning the framework; the same three-layer structure applies when swapping in TypeORM or Prisma later.
Throwing `NotFoundException` (or any built-in NestJS exception) automatically produces a standard JSON error response with the correct HTTP status code — no manual response handling needed.
`@Param()` always returns a string, so numeric IDs must be explicitly converted with `Number()` before passing them to the Service.
Route matching is top-down: a static `@Get()` must be declared before a parameterized `@Get(':id')` to prevent the literal path segment from being captured as a dynamic parameter.
`Test.createTestingModule()` creates a lightweight DI container for unit tests without starting an HTTP server, and providers can be swapped for mocks to isolate the Controller under test.
Conclusions

The tutorial's emphasis on `Partial<Todo>` and `Object.assign` for PATCH operations highlights a common NestJS design choice: the Controller trusts the Service to handle partial updates correctly, keeping HTTP concerns out of business logic.

Using an in-memory array rather than a database is a deliberate pedagogical trade-off that forces attention onto the framework's wiring — the exact same decorator and injection patterns carry over unchanged to real persistence layers, which is the real lesson.

The explicit mapping of exception types to HTTP status codes (400, 401, 403, 404, 500) reveals how NestJS treats HTTP as an output concern of the framework, not something the developer manually constructs in every handler.

Concepts & terms
NestJS three-layer architecture
The standard pattern in NestJS where a Module registers a Controller (handles HTTP routing and parameter extraction) and a Service (contains business logic and data access), enforced through dependency injection.
NestJS exception filters
A built-in mechanism that intercepts thrown exceptions like `NotFoundException` and automatically converts them into standard HTTP responses with the correct status code and JSON error body.
Partial<T>
A TypeScript utility type that makes every property of T optional, commonly used in PATCH endpoints to accept only the fields a client wants to update.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗