How to Properly Structure Your Node.js Project

4 minues

How to Properly Structure Your Node.js ProjectIntroductionStructuring a Node.js project can ...

How to Properly Structure Your Node.js Project


Introduction

Structuring a Node.js project can be challenging, especially given the flexibility the framework offers. Developers often find themselves questioning where to place files, how to separate concerns, and how to ensure scalability. The lack of a universally accepted structure can make it even more confusing, as different articles and projects showcase varied approaches. At MRJ Recruitment, we’ve had countless conversations with software developers navigating the complexities of structuring their projects. 

Rather than prescribing a one-size-fits-all approach, we’re sharing just a couple insights from the market—what real developers are doing, the trends we’re seeing, and common best practices. And, of course, we’re always open to hearing other perspectives!

In this guide, we'll explore best practices for structuring a Node.js project, considering different use cases, including REST APIs, microservices, and workflow automation.


General Principles of Project Structure


1. Keep It Modular

Organise your project into logical modules that group related functionalities together. This promotes maintainability and scalability.

2. Follow the "Rule of 7"

If a directory has more than 7 files or subdirectories, consider breaking it down into smaller submodules.

3. Use Meaningful Directory Names

Clear and intuitive naming helps developers (including your future self) understand where different functionalities reside.

4. Separate Business Logic from Infrastructure

Keep your business logic separate from framework-specific code (e.g., routes, database interactions) to improve testability and reusability.

5. Consider Scalability from Day One

A good structure today should support growth without requiring major rewrites later.



Common Project Structures


1. Traditional MVC Approach

Used in web applications and APIs.

├── src
│   ├── controllers   # Handles HTTP requests
│   ├── models        # Defines database schemas
│   ├── routes        # API routes
│   ├── services      # Business logic
│   ├── middleware    # Express middleware functions
│   ├── utils         # Utility functions
│   ├── config        # Configuration files (e.g., database, environment variables)
│   ├── tests         # Unit and integration tests
│   └── index.js      # Entry point
├── package.json
├── .gitignore
└── README.md

2. Feature-Based (Vertical Slice Architecture)

Useful for large-scale applications where each feature or module is independent.

├── src
│   ├── @users
│   │   ├── user.controller.ts
│   │   ├── user.service.ts
│   │   ├── user.model.ts
│   │   ├── user.routes.ts
│   │   ├── user.repository.ts
│   │   ├── index.ts
│   ├── @products
│   │   ├── product.controller.ts
│   │   ├── product.service.ts
│   │   ├── product.model.ts
│   │   ├── product.routes.ts
│   │   ├── index.ts
│   ├── shared  # Shared modules (e.g., authentication, database connection)
│   ├── config  # Configuration files
│   ├── tests   # Test cases
│   └── index.ts

3. Monorepo with Workspaces

Useful for projects with multiple services or microservices.

├── packages
│   ├── api
│   │   ├── src
│   │   ├── package.json
│   │   └── index.ts
│   ├── workers
│   │   ├── src
│   │   ├── package.json
│   │   └── index.ts
│   ├── shared
│   │   ├── src
│   │   ├── package.json
│   │   └── index.ts
├── package.json
└── yarn.lock



Handling Common Challenges


Where to Place Shared or Generic Functions?

  • If the function is highly reusable across different domains, place it in src/utils/ or src/shared/.
  • If it's specific to a domain but still reusable within that domain, keep it within the respective module (e.g., @sheets/utils.ts).

What About Environment Variables?

  • Store them in a .env file at the root.
  • Use a configuration loader (dotenv, config package) to manage environment variables properly.
  • Avoid committing .env to version control.

Where Should TypeScript Types Go?

For TypeScript projects, maintain types in a dedicated src/types/ directory.

├── src
│   ├── types
│   │   ├── user.types.ts
│   │   ├── product.types.ts



Conclusion

There is no "one-size-fits-all" approach to structuring a Node.js project. The right structure depends on your specific use case, team size, and long-term scalability goals. Whether you prefer MVC, feature-based organisation, or a monorepo setup, the key is to stay consistent, modular, and intuitive.

By following best practices and choosing a structure that suits your project, you’ll ensure better maintainability, ease of collaboration, and scalability. Happy coding!#


Explore More with MRJ Recruitment

At MRJ Recruitment, we’re always exploring the best approaches to software development, and we know how crucial it is to have the right talent in place to bring these structures to life. If you're interested in how top companies are structuring their teams and projects, check out our Case Studies to see real-world success stories.

Looking for insights into .NET development? Visit our .NET Section for expert perspectives on building scalable and efficient applications.

If you're in the world of testing and quality assurance, don't miss our QA Section, where we dive into best practices, automation trends, and the latest in software testing.