# NestJS Fundamentals (Part 2)

## 1. The Module Dependency Graph

Every NestJS app has exactly one root module (conventionally `AppModule`), and every other module reaches the app by being `imports`'d, directly or transitively, from that root — the whole app is a directed graph of modules, and it must be acyclic in the sense that NestJS resolves it at bootstrap into a concrete instantiation order.

```
AppModule
 ├── imports: [UsersModule, OrdersModule]
 │              │              │
 │        exports: [UsersService]   exports: [OrdersService]
 │
 └── OrdersModule also imports UsersModule (to look up a user when creating an order)
```

When `OrdersModule` does `imports: [UsersModule]`, it gains access to whatever `UsersModule` put in its own `exports` array — nothing more. This is the same encapsulation boundary from Part 1 section 4, now shown as the mechanism that keeps a large module graph from turning into "everything can see everything."

## 2. Providers and NestJS's Built-In DI Container

`@Injectable()` classes are managed by NestJS's dependency-injection container, not instantiated with `new` by your own code. When `UsersController`'s constructor declares `private readonly usersService: UsersService`, NestJS's container:

1. Sees `UsersController` needs a `UsersService`.
2. Checks whether it already has an instance of `UsersService` (by default, providers are **singletons** — one instance per application, reused everywhere it's injected).
3. If not, instantiates `UsersService` itself, recursively resolving *its* constructor dependencies the same way, then hands the instance to `UsersController`'s constructor.

```ts
@Injectable()
class UsersService {
  constructor(private readonly db: DatabaseService) {} // DI resolves THIS too, recursively
}
```

This recursive resolution is why forgetting to register a provider (Part 1 Common Pitfall 1) or forgetting to export one (Common Pitfall 2) surfaces as a **bootstrap-time error** (`Nest can't resolve dependencies of ...`) rather than a runtime crash later — the entire dependency graph is validated once, at startup, before the app starts accepting requests. The dedicated `09-dependency_injection/` topic in this group covers DI as a general pattern (including manual/constructor injection outside any framework); this section is specifically about NestJS's own container implementing that pattern.

![A module declares controllers, providers, imports and exports; the DI container reads constructor types and builds singleton instances in dependency order](diagrams/01-module-di-graph.svg)

## 3. The Request Pipeline: Middleware, Guards, Pipes, Interceptors

A request passing through a NestJS route doesn't go straight from "matched" to "handler runs" — it passes through several distinct extension points, each with a specific job, run in this order:

```
Request
  → Middleware        (same concept as Express middleware — 06-middleware/ covers this generally)
  → Guards            (authorization: CAN this request proceed at all? true/false)
  → Interceptors (pre) (wrap the handler: transform input, add logging/timing)
  → Pipes              (validation/transformation of individual arguments — @Body(), @Param(), etc.)
  → Route Handler       (your @Get()/@Post() method itself)
  → Interceptors (post) (transform the handler's return value before it's serialized)
  → Exception Filters    (catch anything thrown anywhere in this pipeline, shape the error response)
```

```ts
import { UseGuards, UsePipes, ValidationPipe } from "@nestjs/common";

@Controller("users")
@UseGuards(AuthGuard) // every route in this controller requires AuthGuard to return true
class UsersController {
  @Post()
  @UsePipes(new ValidationPipe()) // validates @Body() against a DTO's decorators before this runs
  create(@Body() dto: CreateUserDto) {
    // by the time this line runs, dto has already passed validation
  }
}
```

Each stage exists precisely because a single Express middleware function trying to do "auth check, then validate this specific argument, then transform the return value" would tangle three unrelated concerns into one function — NestJS splits them into named, purpose-specific extension points instead. Guards, Pipes, and Interceptors are each covered in more depth by their own relevant topics elsewhere in this curriculum (validation/DTOs, authentication/authorization, exception handling) — this section's job is just to show where each one sits in the overall pipeline.

## 4. Why This Structure Exists at All

A small Express app (`01-express_fundamentals/`) with a handful of routes doesn't need any of this — a few `app.get()` calls and a couple of middleware functions are perfectly manageable by hand. NestJS's entire value proposition shows up at a different scale: once an app has dozens of controllers, shared validation logic, shared auth logic, and multiple teams touching the same codebase, an *unenforced* convention ("controllers should be thin", "always validate input") tends to erode — someone, somewhere, puts business logic in a controller because it was faster that day. NestJS's module/controller/provider/pipe/guard structure makes several of those conventions **mechanically difficult to violate** rather than merely documented as a best practice.

## Common Pitfalls

1. **Manually `new`-ing a provider instead of injecting it.** Bypasses the DI container entirely — you get a fresh, un-managed instance disconnected from the rest of the app's singleton graph, silently defeating dependency injection.
2. **Circular module imports** (`ModuleA` imports `ModuleB` which imports `ModuleA`) — NestJS can resolve some circular cases with `forwardRef()`, but an unhandled cycle fails at bootstrap; the fix is usually to extract the shared piece into a third module both import, not to force the cycle through.
3. **Putting validation logic inside the route handler** instead of a Pipe, or authorization logic inside the handler instead of a Guard — works, but scatters cross-cutting concerns across every handler instead of centralizing them in the pipeline stage built for that job.
4. **Assuming providers are NOT singletons by default.** They are, unless explicitly scoped otherwise (`REQUEST`-scoped, `TRANSIENT`) — code that assumes fresh instance-per-request state in a default-scoped provider will leak state across unrelated requests.

## Quick Reference

```
Root module -> imports -> ... -> every other module reachable in the graph
DI container: providers default to SINGLETON scope, resolved recursively at bootstrap
Bootstrap-time errors: unresolved dependency graph fails FAST, before serving any request

Request pipeline order:
Middleware -> Guards -> Interceptors(pre) -> Pipes -> Handler -> Interceptors(post) -> Exception Filters

Guards    -- can this request proceed? (authorization)
Pipes      -- validate/transform individual arguments (@Body/@Param/@Query)
Interceptors -- wrap the handler call itself (logging, timing, response transformation)
```
