Organizing Your NestJS Code: A Guide to Scalable Folder Structures
Why the default flat src/ falls apart once a project has real features, and the module-driven layout that replaces it — one directory per feature, with common, config and database kept alongside.

When you first create a NestJS project, you’re greeted with a simple and clean folder structure. You have app.controller.ts, app.service.ts, and app.module.ts. For a "Hello World" project, this is more than enough.
But what happens when your project grows? What if you have dozens of endpoints, complex business logic, and various interconnected features? The default structure quickly becomes crowded and messy.
This is where thinking about a scalable folder architecture from the beginning is crucial. This article will guide you through a best-practice approach that keeps your project clean and maintainable: the Module-Driven Structure.
The Problem with a Flat Structure
As you add features like Users, Products, Orders, and Auth, a flat structure in your src folder becomes chaotic:
src/
├── users.controller.ts
├── products.controller.ts
├── orders.controller.ts
├── auth.controller.ts
├── users.service.ts
├── products.service.ts
├── orders.service.ts
└── ... and so onFinding files related to a single feature becomes a chore because its components are scattered. This is inefficient and a nightmare to maintain.
The Solution: A Refined Module-Driven Structure
The core idea is to group all files related to a single feature into their own module. But we can take it one step further for ultimate clarity by creating a dedicated modules directory.
This approach creates a clean separation between your application’s core business logic and its supporting code (like configuration and common helpers).
Here is the highly recommended structure:
src/
├── modules/ # All business logic and features live here
│ ├── auth/ # Everything about authentication & authorization
│ │ ├── strategies/
│ │ ├── guards/
│ │ ├── dto/
│ │ ├── auth.controller.ts
│ │ ├── auth.module.ts
│ │ └── auth.service.ts
│ │
│ ├── products/ # "Products" feature module
│ │ ├── dto/
│ │ ├── entities/
│ │ ├── products.controller.ts
│ │ ├── products.module.ts
│ │ └── products.service.ts
│ │
│ └── users/ # "Users" feature module
│ ├── dto/
│ ├── entities/
│ ├── users.controller.ts
│ ├── users.module.ts
│ └── users.service.ts
│
├── common/ # Reusable components (pipes, filters, etc.)
│
├── config/ # Configuration management
│
├── database/ # Migrations, seeds, etc.
│
├── app.module.ts # Root module, where all feature modules are imported
└── main.ts # Application entry pointBreaking Down the Structure
Let’s look at the role of each top-level directory:
1. modules/
This is the heart of your application. Each sub-directory represents a self-contained feature or business domain.
- *.module.ts: Defines the module for the feature, importing the controller and registering providers (services).
- *.controller.ts: Handles routing, receives requests, and calls the service.
- *.service.ts: Contains all the complex business logic.
- entities/: (If using an ORM like TypeORM) Contains the data model definitions or database schemas.
- dto/ (Data Transfer Object): Contains classes that define the "shape" of data sent between the client and server. This is crucial for validation.
2. common/
This folder is your project’s “toolbox.” All reusable components, functions, or classes that can be used across multiple features are placed here. Examples include a custom decorator to get user data from a request, or an exception filter to handle errors consistently across the entire application.
3. config/
Never hardcode database credentials or API keys. NestJS has an excellent @nestjs/config module for managing environment variables. This folder is the place to house all that configuration logic.
4. database/
If your project uses database migrations or seeding, this folder is the perfect place to keep them organized and separate from your application logic.
Benefits of This Structure
- Clarity & Organization: It’s immediately clear where the core business logic of your application lives (modules) versus the supporting code.
- Scalability: Adding a new feature is as easy as creating a new folder inside modules without cluttering the root directory.
- Maintainability: Code is easier to find, understand, and fix. Need to fix a bug in the Orders feature? Just open src/modules/orders/.
- Team Collaboration: Different developers can work on different feature modules in parallel with a lower risk of conflicts.
Conclusion
Adopting a robust folder structure from the start is a long-term investment in your project’s health. By grouping your features inside a dedicated modules directory, you create a clean, scalable, and highly maintainable architecture that will serve you well as your application grows.
Start organizing your code this way, and your future self will thank you for it! Happy coding!


