Folder Structure for NodeJS & ExpressJS project

Vaibhav Mehta
6 min readNov 1, 2022
NodeJS Folder Structure

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

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);

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

--

--

Vaibhav Mehta

Engineering Manager @ BrowserStack • 🎮 Gamer • 📷 Amateur Photographer • Design • 🚀 Space • 🍿Popcorn Lover • JSONBin.io Author • More about me @ 8bit.codes