NestJS / Basic: modules and request contracts / Lesson 1
Application architecture and adapter boundaries
Place Nest modules and application services above the selected transport adapter and keep process startup explicit.
On this page
Lesson goals
- Separate the module graph, dependency container and HTTP adapter.
- Explain what a local domain model proves without bootstrapping Nest.
A framework structure does not supply domain guarantees
Nest organizes a Node.js application around modules, controllers and providers. Modules describe composition, controllers adapt incoming transport work and providers supply reusable capabilities. The dependency container constructs and connects registered instances; it is not a database or an authorization policy. A course portal can expose the same publication use case through HTTP and a worker without copying its business rules. Start by naming the use case, its permitted callers and authoritative data. Decorators make framework relationships visible, but they cannot prove that a public endpoint returns the correct tenantโs record.
Read composition without starting a service
The module fragment registers one controller and one service. It assumes their classes already exist and does not include imports, compilation settings or bootstrap code. NestFactory.create and an eventual listen belong to an explicit process entry point; importing a domain module should not open a port or run migration jobs. The container can only construct a valid graph when every required dependency is registered and visible. This course studies such framework fragments without installing Nest or creating an application context, while separately executing complete plain JavaScript models.
@Module({
controllers: [CoursesController],
providers: [CatalogService],
})
export class CatalogModule {}
The metadata describes composition only. The classes, decorators and Nest runtime are assumed and were not executed.
Choose the HTTP platform deliberately
Nest can use different HTTP platform adapters. Express-specific request and response methods do not become portable merely because a controller has Nest decorators. Prefer framework-managed return values for ordinary handlers and isolate platform-specific APIs where they are genuinely needed. A fast adapter benchmark does not establish application correctness or eliminate source bottlenecks. Keep routing grammar, body parsing, proxy trust, cookie behavior and streaming assumptions tied to the chosen platform and version. A deployment change from one adapter to another needs retained request fixtures rather than a dependency-name substitution.
Keep public projection independent of the framework
The complete local projection returns only the fields intended for a public course card. It does not call a container or network and deliberately omits an internal property. This makes public shape testable before a controller exists. Eligibility still belongs to a prior domain policy: projecting a private course does not authorize publishing it. A real serializer may provide additional declarative behavior, but should preserve the same reviewed output contract. Avoid returning a whole persistence entity just because the framework can serialize ordinary objects automatically.
function card(record) {return {id:record.id,title:record.title};}
const result=card({id:"c1",title:"Module foundations",internalNote:"lab-only"});
console.assert(JSON.stringify(result) === '{"id":"c1","title":"Module foundations"}');
console.assert(!Object.hasOwn(result,"internalNote"));
Both assertions pass for the synthetic record. They verify output shape only, without authorization or a Nest runtime.
Record version and execution assumptions
This course selects the Nest 12 documentation baseline reviewed on 2026-10-28, while keeping examples to established module and request concepts. Current migration guidance distinguishes application runtime requirements from higher CLI/schematics requirements and documents ESM core packages. Do not assume a runtime that can execute a local JavaScript function can scaffold or test every Nest project. Pin the exact packages, module mode, compiler and test runner before integration. All framework blocks remain runnable:false; local model verification is not a claim that bootstrap, platform hooks or dependency resolution were exercised.
Common errors and fixes
Importing a module opens a listener
- Why it happens
- Bootstrap side effects were mixed into domain composition.
- How to fix it
- Separate module definitions from the process entry point.
- Prevent it next time
- Keep import-time code free of operational work.
An adapter swap breaks response behavior
- Why it happens
- Platform-specific methods were treated as portable Nest APIs.
- How to fix it
- Isolate platform calls and verify retained transport fixtures.
- Prevent it next time
- Record adapter and package versions in review.
Private fields appear in a public result
- Why it happens
- The persistence entity was serialized wholesale.
- How to fix it
- Project allowed fields after eligibility checks.
- Prevent it next time
- Assert exact public keys and denied-object behavior.
Check your understanding
1. Does @Module metadata prove that CatalogService can be constructed?
Show worked solution
No. They declare registration, but all constructor dependencies must also exist and be visible under the module graph. A container compilation test can verify that wiring using fakes; the displayed fragment was not compiled. Keep registration intent separate from runtime dependency-resolution evidence.
2. Why keep a projection function outside an HTTP controller?
Show worked solution
It makes the public shape reusable and testable without bootstrapping a transport. Another adapter can reuse the same domain output policy. The controller still owns status and request mapping, and the domain still owns access eligibility; the pure function should not be advertised as proving those other boundaries.
Key takeaways
- Modules compose capabilities; domain rules and platform behavior remain explicit contracts.
- Local model evidence is narrower than a compiled Nest integration.