DEV Community

Cover image for Recommended Folder Structure for Node(TS) 2025
Pramod Boda
Pramod Boda

Posted on

Recommended Folder Structure for Node(TS) 2025

If you want ReactJS Folder Structure, You can get from here: Recommended Folder Structure for React 2025


A common and effective folder structure for a Node.js web application with Express in 2025 is based on the service-layered architecture or feature-based organization, which promotes separation of concerns, scalability, and maintainability.

In this article, I have explained both feature-based organization and service-layered architecture, Now by end of this article, You will choose one. I personally follow feature-based architecture. Tell me in comments which one you liked?

Now, lets see how to structure our node projects, Lets go...

Key Principles of a Modern Node.js Structure πŸ—οΈ

The goal is to move beyond a simple, monolithic app.js file and organize code into logical, reusable components. This helps with:

  • Scalability: The application can grow without becoming a tangled mess.
  • Maintainability: It's easier to find, debug, and update specific parts of the code.
  • Testing: Individual units of code can be tested in isolation.
  • Team Collaboration: Multiple developers can work on different parts of the application without conflicts.

Recommended Feature-Based Structure (I personally follow this)

Using feature-based organization is an excellent and modern approach to structuring a Node.js Express application, especially for larger projects. This method groups related files (controllers, services, models) by domain or feature rather than by type. This makes the codebase more scalable, easier to navigate, and better for team collaboration.


This structure is a direct evolution of the layered approach, organizing the same components but in a more modular way.

pramodboda-app/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ features/
β”‚   β”‚   β”œβ”€β”€ auth/            # All files related to user authentication
β”‚   β”‚   β”‚   β”œβ”€β”€ auth.controller.ts   # Request handlers: process input and call 'auth' services
β”‚   β”‚   β”‚   β”œβ”€β”€ auth.model.ts        # Database schemas and data models of 'auth' related
β”‚   β”‚   β”‚   β”œβ”€β”€ auth.route.ts        # Define API endpoints of 'auth' and map them to controllers
β”‚   β”‚   β”‚   └── auth.service.ts      # Core business logic; the "brain" of the 'auth' features
β”‚   β”‚   β”‚
β”‚   β”‚   β”œβ”€β”€ users/           # All files related to user management
β”‚   β”‚   β”‚   β”œβ”€β”€ user.controller.ts   # Request handlers: process input and call 'users' services
β”‚   β”‚   β”‚   β”œβ”€β”€ user.model.ts        # Database schemas and data models of 'users' related
β”‚   β”‚   β”‚   β”œβ”€β”€ user.route.ts        # Define API endpoints of 'users' and map them to controllers
β”‚   β”‚   β”‚   └── user.service.ts      # Core business logic; the "brain" of the 'users features
β”‚   β”‚   β”‚
β”‚   β”‚   └── products/        # All files related to products
β”‚   β”‚       β”œβ”€β”€ product.controller.ts  # Request handlers: process input and call 'product' services
β”‚   β”‚       β”œβ”€β”€ product.model.ts       # Database schemas and data models of 'products' related
β”‚   β”‚       β”œβ”€β”€ product.route.ts       # Define API endpoints of 'products' and map them to controllers
β”‚   β”‚       └── product.service.ts     # Core business logic; the "brain" of the 'products' features
β”‚   β”‚
|   β”œβ”€β”€ api/                # API entry points (e.g., v1/)
β”‚   β”œβ”€β”€ config/             # Centralized configuration files (DB, auth, etc.)
β”‚   β”œβ”€β”€ middleware/         # Custom Express middleware (auth, logging, etc.)
β”‚   β”œβ”€β”€ utils/              # Shared helper functions and reusable code
β”‚   β”‚
β”‚   └── index.ts            # The main entry point for the application logic
|
β”œβ”€β”€ tests/                  # All unit and integration tests
β”œβ”€β”€ .env                    # All variable environments for App
β”œβ”€β”€ .gitignore              # This file includes what are all file/ folders should not to move to your Git repo
β”œβ”€β”€ package.json
β”œβ”€β”€ server.ts               # Sets up and starts the server
└── tsconfig.json
Enter fullscreen mode Exit fullscreen mode

Why is this a good idea?

  • Cohesion: All code for a specific feature (e.g., users) is located in one place, making it easy to understand and work on that feature in isolation.
  • Scalability: As your application grows, you simply add new folders for new features without cluttering a central controllers/ or services/ folder.
  • Maintainability: You can delete an entire feature folder when it's no longer needed without affecting the rest of the application.

Breakdown of Key Folders

This folder structure uses a feature-based organization, which groups all related code for a specific domain together. This approach is highly scalable and maintains a clear separation of concerns. Here's a breakdown of the key folders and files in your provided structure:

src/ Folder

This is the main directory for your application's source code. Keeping all source files here helps keep the project root clean.

  • features/: This is the core of your application's modular design. Each subfolder here represents a distinct domain or "feature" of your application, like auth or users. This approach, known as Domain-Driven Design (DDD), ensures that all components related to a specific business function are co-located.
    • auth/, users/, products/: These folders are your features. Each one contains the controller, model, route, and service files specific to that feature. This prevents your codebase from becoming a monolithic mess as it grows. For example, all code for managing users lives in features/users/.
      • auth.controller.ts: Handles requests related to authentication. It takes the request, validates the input, and calls the auth.service.ts to execute the business logic, then sends the response back to the client.
      • auth.model.ts: Defines the structure of your data in the database for the authentication feature (e.g., the User schema in Mongoose).
      • auth.route.ts: Defines the API endpoints for authentication (e.g., /api/auth/login, /api/auth/register) and maps them to the appropriate controller functions.
      • auth.service.ts: Contains the business logic for the feature. This is the "brain" of your feature, where complex operations like password hashing, JWT token creation, and database interactions happen. The controller simply orchestrates the service calls.
  • api/: A common practice for versioning your API (e.g., v1/). You could move your route files into src/api/v1/ to support multiple API versions in the future.
  • config/: Contains all configuration files for your application, such as database connection settings, API keys, or environment-specific variables.
  • middleware/: Houses all your custom Express middleware functions, which can be shared across multiple features. Examples include authentication checks, error handlers, and request loggers.
  • utils/: A repository for reusable helper functions that don't belong to any specific feature or layer. Examples include validation helpers, email sending functions, or general utility functions.
  • index.ts: The central file that sets up your Express application. It imports and applies all global middleware (like express.json() and cors) and registers all your feature routes. This file acts as a central hub for your application's logic, but it doesn't start the server itself.

Root Level Files & Folders

  • tests/: A dedicated folder for all your unit and integration tests. Organizing them here keeps them separate from your production code.
  • .env: Stores environment-specific variables. This file should never be committed to version control.
  • .gitignore: Specifies files and folders that Git should ignore, such as node_modules and .env.
  • server.ts: The entry point for your application. Its sole responsibility is to import the Express app from index.ts and start the server, listening on a specific port. This separation makes your index.ts file highly testable, as you can import the app instance without starting the server.
  • tsconfig.json: The configuration file for the TypeScript compiler, which defines how your TypeScript code should be compiled into JavaScript.
  • package.json: Manages your project's metadata and dependencies.

server.ts and app.ts (or index.ts)

It's a common and good practice to separate the Express application setup from the server listening part. This makes your application more testable, as you can import the Express app instance directly into your test files without needing to start a server.

src/server.ts

This file is the entry point for starting the server. It's minimal and only concerns itself with initializing the Express application and listening for incoming requests.

// src/server.ts
import { app } from './index'; // or './app' if you use app.ts
import { connectDB } from './config/database'; // Assuming you have a DB config

const PORT = process.env.PORT || 5000;

// Connect to the database before starting the server
connectDB();

app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});
Enter fullscreen mode Exit fullscreen mode

src/index.ts (or app.ts)

This file is the core of your application. It's where you configure all your middleware, define global settings, and import and register all your feature routes.

// src/index.ts
import express from 'express';
import cors from 'cors';
import helmet from 'helmet';
import compression from 'compression';

import authRoutes from './features/auth/auth.route';
import userRoutes from './features/users/user.route';
// Import other feature routes here

export const app = express();

// Global Middleware
app.use(express.json());       // Parse JSON body
app.use(express.urlencoded({ extended: true })); // Parse URL-encoded bodies
app.use(cors());               // Enable Cross-Origin Resource Sharing
app.use(helmet());             // Secure HTTP headers
app.use(compression());        // Compress response bodies

// API Routes
app.use('/api/auth', authRoutes);
app.use('/api/users', userRoutes);
// Use other feature routes here
Enter fullscreen mode Exit fullscreen mode

This separation makes the application's core logic (index.ts) highly modular and testable, while server.ts remains a simple, clean entry point for deployment.

This structure allows for a clear separation of concerns, making your application more modular, robust, and easier to maintain as it grows.


Now, the below Service-layered Folder Structure is bit common and confusion for me. If you like the Service-layered Folder Structure then use the below one:

Service-layered Folder Structure

This structure separates code by its purpose and domain, often combining elements of the Model-View-Controller (MVC) pattern with a service-layer approach.

my-web-app/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ api/             # API entry points (e.g., v1/)
β”‚   β”œβ”€β”€ config/          # Centralized configuration files (DB, auth, etc.)
β”‚   β”œβ”€β”€ controllers/     # Request handlers: process input and call services
β”‚   β”œβ”€β”€ middleware/      # Custom Express middleware (auth, logging, etc.)
β”‚   β”œβ”€β”€ models/          # Database schemas and data models
β”‚   β”œβ”€β”€ routes/          # Define API endpoints and map them to controllers
β”‚   β”œβ”€β”€ services/        # Core business logic; the "brain" of the app
β”‚   └── utils/           # Shared helper functions and reusable code
β”‚
β”œβ”€β”€ public/              # Static files (CSS, JS, images)
β”œβ”€β”€ tests/               # All unit and integration tests
β”œβ”€β”€ .env                 # Environment variables for local development
β”œβ”€β”€ .gitignore
β”œβ”€β”€ package.json
β”œβ”€β”€ server.js            # Main application entry point
└── README.md
Enter fullscreen mode Exit fullscreen mode

Breakdown of Key Folders

  • src/: This is the heart of your application. All your source code lives here, keeping the project root clean.
  • config/: This folder centralizes all your application configurations. Never hardcode sensitive information like API keys or database credentials. Use a .env file and a library like dotenv to manage them.
  • controllers/: These files are responsible for handling incoming HTTP requests. A controller's job is simple: take the request, validate the data, call the appropriate service to perform the business logic, and send a response. They should contain minimal logic.
  • middleware/: Store custom Express middleware here. This is where you'll put functions for things like authentication, error handling, request logging, or rate limiting.
  • models/: In a database-driven application, this is where you'd define your database schemas (e.g., with Mongoose for MongoDB or Sequelize for a SQL database). These models provide a structured way to interact with your data.
  • routes/: This folder contains the definitions of all your API endpoints. Each file should group related routes together (e.g., user.routes.js, product.routes.js). The routes file's sole purpose is to map an HTTP method and path to a controller function.
  • services/: This is a crucial folder that contains all your core business logic. This layer is responsible for complex operations, interacting with models, and orchestrating tasks. By separating this logic from the controllers, you make your code reusable and much easier to test.
  • utils/: This is a catch-all for small, reusable helper functions that don't fit into any other category. Examples include functions for password hashing, token generation, or data formatting.
  • public/: If your web app serves static assets, like CSS, JavaScript files, or images, put them here. This folder is typically exposed directly to the browser.
  • tests/: A dedicated folder for all your unit and integration tests. Organize them to mirror your src directory to make it easy to find tests for specific components.

Enjoyed this post?

Stay updated with the latest tech trends! Follow me on Instagram: @pramodboda.art and @pramodboda.codevik.
let me know your thoughts! πŸ‘‡

Top comments (0)