DEV Community

Cover image for Folder Structure for NodeJS & ExpressJS project
Vaibhav Mehta
Vaibhav Mehta

Posted on

Folder Structure for NodeJS & ExpressJS project

Prelude

I've worked over several backend technologies, starting from PHP, then moving to RoR before getting my hands on NodeJS. I love how NodeJS simplifies backend development for Frontend developers; and not only this, the NPM ecosystem helps devs get up and running with complex projects with ease, without the need of re-inventing the wheel by developing core packages.

Over these years, I've tried and tested various folder structures for my projects (confession: I've never referred to any folder structure on the web as I wanted to design something of my own which I find comfortable working with rather than being biased upfront - how ironic as I am drafting this tutorial ๐Ÿ™‚) and I've finally reached a stage where I feel confident enough to share something which works for a majority of my NodeJS projects.

As an example for this article, we will try to structure an App assuming there's a:

  1. Website with a Landing Page
  2. Basic Authentication
  3. A Dashboard
  4. You are using Express like framework
  5. Build tools for Frontend like Gulp, Webpack.
  6. Applicable if you are using Frontend frameworks/libs like React, Angular, Vue.js, etc.
  7. Applicable if you wish to build API only App.

That's a pretty standard scenario for any application out there.

Last but not the least, the structure is more or less a Monorepo. You could take some good parts and apply them to your apps if your Node Apps are intended to serve as an API only or as a Website.


NodeJS Project Folder Structure

The folder structure I usually follow is inspired massively by RoR as I was working with Ruby & RoR before switching to NodeJS.

Here's how I structure my NodeJS App. I'll be explaining some of the reasoning behind the file structure and will also share some snippets on how do I expose some of the configs at a global level or how do I initiate a Database Connection across the app.

/app
โ”œโ”€โ”€ config/
โ”‚   โ”œโ”€โ”€ db.conf.js
โ”‚   โ”œโ”€โ”€ app.conf.js
โ”‚   โ”œโ”€โ”€ app.keys.js
โ”‚   โ”œโ”€โ”€ db.keys.js
โ”‚   โ”œโ”€โ”€ init.js
โ”œโ”€โ”€ database/
โ”‚   โ”œโ”€โ”€ Redis.database.js
โ”‚   โ”œโ”€โ”€ Mongo.database.js
โ”‚   โ”œโ”€โ”€ init.js
โ”œโ”€โ”€ routes/
โ”‚   โ”œโ”€โ”€ App.routes.js
โ”‚   โ”œโ”€โ”€ Auth.routes.js
โ”‚   โ”œโ”€โ”€ Dashboard.routes.js
โ”œโ”€โ”€ utils/
โ”‚   โ”œโ”€โ”€ Logger.util.js
โ”œโ”€โ”€ middleware/
โ”‚   โ”œโ”€โ”€ App.middleware.js
โ”‚   โ”œโ”€โ”€ ErrorHandler.middleware.js
โ”‚   โ”œโ”€โ”€ init.js
โ”œโ”€โ”€ models/
โ”‚   โ”œโ”€โ”€ User.model.js
โ”œโ”€โ”€ controllers/
โ”‚   โ”œโ”€โ”€ App.controller.js
โ”‚   โ”œโ”€โ”€ User.controller.js
โ”œโ”€โ”€ helpers/
โ”‚   โ”œโ”€โ”€ App.helper.js
โ”œโ”€โ”€ views/
โ”‚   โ”œโ”€โ”€ layouts/
โ”‚   โ”œโ”€โ”€ partials/
โ”‚   โ”œโ”€โ”€ support/
โ”‚   โ”‚   โ”œโ”€โ”€ index.ejs
โ”‚   โ”œโ”€โ”€ documentation/
โ”‚   โ”‚   โ”œโ”€โ”€ index.ejs
โ”‚   โ”œโ”€โ”€ index.ejs
โ”‚   โ”œโ”€โ”€ about.ejs
โ”‚   โ”œโ”€โ”€ contact.ejs
/public
โ”œโ”€โ”€ dist/
โ”œโ”€โ”€ images/
โ”‚   โ”œโ”€โ”€ dashboard/
โ”‚   โ”œโ”€โ”€ auth/
โ”‚   โ”œโ”€โ”€ documentation/      
โ”œโ”€โ”€ sitemap.xml
/samples
โ”œโ”€โ”€ .env.sample
โ”œโ”€โ”€ db.conf.sample
โ”œโ”€โ”€ app.conf.sample
โ”œโ”€โ”€ app.keys.sample
/src
โ”œโ”€โ”€ javascript/
โ”œโ”€โ”€ css/
/node_modules
/server.js
/package.json
/.env
Enter fullscreen mode Exit fullscreen mode

Project Structure Brief

Config & Sample

Configuration could be categorised into three major categories:

  1. System Configuration
  2. App Configuration
  3. App Keys

Store the configuration variables in .env (dotenv) which are necessary for configuring your application like App Port, Environment (Production, Staging, etc.). .env configuration will be available across your app as they are set globally as ENV variables.

You could then create multiple app-relevant configuration files like:

  • Database Connection: Database-specific configurations like Host, Port & Name.
  • App Configuration: Acceptable Request Payload Size, Blacklisting IPs or Regions, etc.
  • Auth Configuration: OAuth Callback URLs, etc.

And last but not the least, you could create multiple files and store relevant keys, like OAuth Client & Secret Keys, Database Keys, Mailer Keys etc.

Important: Do not commit any of these configuration files to Git. Copy the configuration structure with dummy values and commit to git using sample files under the /sample folder.


Database

This is where you could create connections to your database(s) like Redis, MySQL, MongoDB etc.

You could then require the necessary configuration & keys to initiate a connection here. This will also make it easier for you manage everything related to your Database connection in one file, like adding listeners around successful connections, errors and disconnects, etc.

Later, these connectors could be loaded under initialization files as needed.


Routes

Usually, you could create a single Route file here to manage all your app related routes but it is recommended to create multiple route files which could be categorised like:

  • Website Routes
  • API Routes
  • Authentication Routes
  • Documentation Routes

etc. Above is more scalable as well as manageable. Here's a code sample for the above:

const express = require("express");
...
const websiteRoutes = require("@routes/Website.routes");
const apiRoutes = require("@routes/Api.routes");

...

app.use("/", websiteRoutes);
app.use("/api", apiRoutes);
Enter fullscreen mode Exit fullscreen mode

As you see, moving the routes to separate files also simplifies how your app would consume these routes.


Utils

Your app may have several utility functions, like Logging important information, or something which are static and have no relevance to other classes/files, as opposed to Helpers (defined later in this post) which may help other classes or modules in your app.


Middleware

Your app will have several middleware functions which could be separated and initiated in a single file. These helpers could range from critical middleware like Body Parser, Global Error Handlers, Authentication Middleware, enabling CORS, Attaching Custom Headers or setting a View Engine in ExpressJS.


Models

Every Collection (if MongoDB) or a Table (if MySQL) will have a standalone model file. For eg: A collection of Users will have it's own User.model.js file which could be extended further for defining a Schema Structure for the collection, Setting Default Values in the DB or Validating the User Inputs before storing the values to Database.


Controllers

Controllers will be responsible to handle all the incoming requests to your application which will either render a page in response, may send a JSON payload or will handle other critical API related actions like POST, PUT, DELETE etc.

Controllers will further consume Models, say a User model to Create Users while registering on your website or will render the view files if static pages are requested.


Helpers

Unlike utility methods, helpers could be dynamic in nature and related to specific controllers when needed. Helpers may contain methods to parse some user posted payload, modify it before storing it to the Database, etc.


Views

You may not need this folder if you are developing an API only app or using a separate SSR library like Next or Nuxt. This may be useful when you are using Express View Engine like Pug or EJS.

You can further divide your views into Layouts, Pages & Partials. Where, Layouts could be shared between similar pages and a website could have multiple layouts. Lastly, partials will hold common components like Header, Footer, Sidebar etc which are needed across your web pages.


Public

As the name suggests, anything under this folder will be publicly accessible by your website users. CSS, JavaScript and Images will be a part of this folder.

Your app may use tools like Webpack and Gulp which will compile(minify, pre-process) your (S)CSS & JS files and move under the public folder which will later be linked on web pages.

You could also build and output your React (or similar lib/framework) app under the dist/ folder.


Src

As App folder is responsible for handling all of the backend logic, Src folder will hold your JavaScript & CSS files if you are using SASS, ES 6 / Next which needs to be transpiled, compiled, minified and uglified before these could be served to the end users.

Src folder will further hold directories like CSS and JavaScript which could be customized as per your needs.


A simple explanation for init.js files before I close this article, init.js files will require rest of the files and export them to other files of your application, so that you need not have to require multiple files every time if you wish to consume them all.


Conclusion

I follow the above folder structure more or less in all the projects I build on Node. It works well for me and I would love to hear from you if it helps you in some way.

Eventually, you should try and modify the folder structure as per your needs and something which you find comfortable managing and scaling it in the long run.


Useful Resources

Lastly, I would love to share some packages which I use to manage configuration, simplifying the process of requiring files at various nested levels, as well as some Linting packages to standardise your code to a great extent..

  1. DotENV
  2. module-alias
  3. Prettier
  4. ESLint

Latest comments (61)

Collapse
 
yeasin2002 profile image
Md Kawsar Islam Yeasin

I don't know way I don't love this kind of folder structure where route, controller are not at the same file

Here What I prefer:

`

project-root/
โ”‚
โ”œโ”€โ”€ src/
โ”‚ โ”œโ”€โ”€ api/ # Group controllers, routes, and validation by feature
โ”‚ โ”‚ โ”œโ”€โ”€ user/
โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ user.controller.ts # User controller
โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ user.route.ts # User routes
โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ user.validation.ts # User input validation (optional)
โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ user.service.ts # User-specific services
โ”‚ โ”œโ”€โ”€ database/
โ”‚ โ”‚ โ”œโ”€โ”€ Redis.database.js
โ”‚ โ”‚ โ”œโ”€โ”€ Mongo.database.js
โ”‚ โ”‚ โ””โ”€โ”€ auth/
โ”‚ โ”‚ โ”œโ”€โ”€ auth.controller.ts # Auth controller
โ”‚ โ”‚ โ”œโ”€โ”€ auth.route.ts # Auth routes
โ”‚ โ”‚ โ”œโ”€โ”€ auth.service.ts # Auth service
โ”‚ โ”‚ โ””โ”€โ”€ auth.validation.ts # Auth validation (optional)
โ”‚ โ”‚
โ”‚ โ”œโ”€โ”€ config/ # App configuration (environment, database, etc.)
โ”‚ โ”‚ โ”œโ”€โ”€ database.ts # Database connection
โ”‚ โ”‚ โ”œโ”€โ”€ env.ts # Environment variable configuration
โ”‚ โ”‚ โ””โ”€โ”€ logger.ts # Logger configuration
โ”‚ โ”‚
โ”‚ โ”œโ”€โ”€ middlewares/ # Custom middleware (authentication, error handling)
โ”‚ โ”‚ โ”œโ”€โ”€ error.middleware.ts # Centralized error handling
โ”‚ โ”‚ โ”œโ”€โ”€ auth.middleware.ts # Auth middleware for protected routes
โ”‚ โ”‚ โ””โ”€โ”€ validate.middleware.ts # Validation middleware for request schemas
โ”‚ โ”‚
โ”‚ โ”œโ”€โ”€ models/ # Mongoose/Sequelize models or DB schemas
โ”‚ โ”‚ โ”œโ”€โ”€ user.model.ts # User model (Mongoose, Sequelize, etc.)
โ”‚ โ”‚ โ””โ”€โ”€ auth.model.ts # Auth-related model (tokens, sessions, etc.)
โ”‚ โ”‚
โ”‚ โ”œโ”€โ”€ services/ # Business logic and reusable services
โ”‚ โ”‚ โ”œโ”€โ”€ email.service.t # Email service (send emails)
โ”‚ โ”‚ โ”œโ”€โ”€ auth.service.ts # Authentication and authorization service
โ”‚ โ”‚ โ””โ”€โ”€ user.service.ts # User-related services (CRUD operations)
โ”‚ โ”‚
โ”‚ โ”œโ”€โ”€ utils/ # Helper functions/utilities (non-business logic)
โ”‚ โ”‚ โ”œโ”€โ”€ httpResponse.ts # Standardized response format
โ”‚ โ”‚ โ”œโ”€โ”€ constants.ts # App constants
โ”‚ โ”‚ โ””โ”€โ”€ hash.ts # Password hashing utility
โ”‚ โ”‚
โ”‚ โ”œโ”€โ”€ validations/ # Centralized validation schemas (using Zod, Joi, etc.)
โ”‚ โ”‚ โ”œโ”€โ”€ user.validation.ts # User-related validation
โ”‚ โ”‚ โ””โ”€โ”€ auth.validation.ts # Auth validation
โ”‚ โ”‚
โ”‚ โ”œโ”€โ”€ app.ts # Initialize Express app
โ”‚ โ””โ”€โ”€ index.ts # Main entry point to start the server
โ”‚
โ”œโ”€โ”€ dist/ # Compiled JavaScript files (from TypeScript)
โ”‚
โ”œโ”€โ”€ node_modules/ # Dependencies
โ”‚
โ”œโ”€โ”€ .env # Environment variables
โ”œโ”€โ”€ .eslintignore # ESLint ignore patterns
โ”œโ”€โ”€ .eslintrc.json # ESLint configuration
โ”œโ”€โ”€ .gitignore # Ignore node_modules and dist
โ”œโ”€โ”€ package.json # Project dependencies and scripts
โ”œโ”€โ”€ tsconfig.json # TypeScript configuration
โ””โ”€โ”€ README.md

`

Collapse
 
mrlazyprogrammer profile image
Smruti Ranjan Badatya

For the most part, you have a great structure. I have something to add on to the structure. Basically, we can follow something called domain-driven development. By doing this we can make the codebase a lot more maintainable. You can read about it in this blog post.

Scalable Directory Structure for NodeJS + Express Web Servers

Collapse
 
aditi_shukla_9bbf207a15ca profile image
Aditi Shukla

A detailed explanation. It taught me a lot as a NodeJS begginer.

Collapse
 
esha_lal profile image
Esha Lal

Your guide promotes a clear and structured approach to Node.js project organization, making it easier for developers to maintain and scale their applications.

Collapse
 
shruti_rai profile image
Shruti Rai

Great explanation

Collapse
 
sampritym profile image
Samprity Mukherjee

very useful

Collapse
 
narendranegi profile image
Neeraj negi

๐Ÿ™‚ nice

Collapse
 
shruti_tagade profile image
Shruti Tagade

Well explained! The structure is clear and easy to follow. Thanks for sharing!

Collapse
 
dinesh_41916 profile image
Kuddana Dinesh

A good folder structure in your Node JS and Express JS project helps keep your code organized and easy to debug. It makes your project clearer and simpler to work with. Thank you for this blog!

Some comments may only be visible to logged-in visitors. Sign in to view all comments.