Bootstrapping & Project Structure
Bootstrapping & Project Structure
A freshly generated NestJS project has a predictable layout. Understanding how the app boots — and what each generated file does — makes everything that follows easier. Every NestJS application starts from a single entry file and a single root module.
The entry point: main.ts
src/main.ts is where the application is created and starts listening. It uses the NestFactory to build an application instance from the root module:
NestFactory.create() takes the root module and returns a fully wired application: it reads every module, instantiates providers, resolves dependencies, and registers controllers. app.listen(3000) then starts the HTTP server.
app instance here, before listen().
The root module: app.module.ts
The root module is the tree's trunk. Every other module is imported into it (directly or indirectly):
The @Module() decorator takes a metadata object with four possible keys:
- imports — other modules whose exported providers this module needs.
- controllers — controllers instantiated by this module.
- providers — services available for injection inside this module.
- exports — providers this module shares with modules that import it.
Standard folder layout
users feature gets its own folder containing users.module.ts, users.controller.ts, users.service.ts, and a dto/ folder — everything about users in one place.
The file-naming convention
NestJS names files name.type.ts: users.controller.ts, users.service.ts, users.module.ts. This is not cosmetic — the CLI relies on these suffixes when generating and wiring files, and it makes large projects instantly navigable.
providers array, NestJS cannot inject it and throws a "Nest can't resolve dependencies" error at startup. Using nest g service avoids this by registering it for you.
Summary
A NestJS app boots from main.ts, which uses NestFactory.create() to build the app from the root AppModule. Modules declare their controllers and providers via the @Module() decorator, and code is organised by feature using the name.type.ts convention. Next we will dive into controllers — the entry point for every request.