DEV Community

nermineslimane
nermineslimane

Posted on

How to structure your Express and Node.Js project

Why is project architecture so crucial?

Having a proper foundation for our project architecture is critical for the project's longevity as well as your capacity to react to changing needs in the future. A faulty, unorganized project architecture frequently results in:

  • Unreadable and messy code slows down development and makes testing the product more difficult.
  • Repetition is inefficient and makes code more difficult to maintain and manage.
  • It's difficult to add new features without breaking old code.

The primary goal of any Node.js project structure is to assist you in:

  • Create code that is both tidy and readable.
  • Create code that can be reused across our program.
  • Repetition should be avoided.
  • New features can be added without affecting current code.

There is no correct or wrong way !

In the end, I don't think there's a single optimum project structure for Express projects.

Rather than asking, "How should I organize my files and folders?"
I believe it is more appropriate to ask, "Where do my various sorts of logic go?"
There are more specific answers to that issue, as well as steps we may take.
And, if we do a good job of layering our logic, a project structure will develop automatically.
A structure that can be organized in whatever way you want, whether it's using more classic MVC (or MVC-ish) or the cool new kid, components. This is due to the fact that these structures will be layered in either case! Simply place the routes, controllers, services, and other items in a component folder.

The idea is to use the principle of separation of concerns, to move the business logic away from the node.js API Routes.

Here's what i would suggest :

│   app.js          # App entry point
└───routes          # Our routes controllers for all the endpoints of the app
└───config          # Environment variables and configuration related stuff
└───controllers     # Functions for our APIs
└───models          # Database models
└───middlewares     # Contains all the middleware that we need
└───utils           # Common functions that would be used repetitively
Enter fullscreen mode Exit fullscreen mode

Let's get right in and learn more about the project's structure.

  • /Controllers- This folder would contain all the functions for your APIs. Naming of files- xxxxx.controllers.js

  • /Routes- This folder would contain all the routes that you have created using Express Router and what they do would be exported from a Controller file
    Naming of files- xxxxx.routes.js

  • /Models- This folder would contain all your schema files and and the functions required for the schema would also lie over here.
    Naming of files- xxxxx.js

  • /Middleware- This folder would contain all the middleware that you have created, whether it be authentication/some other function.
    Naming of files- xxxxx.middleware.js

  • /Utils(Optional)- The common functions that you would require multiple times throughout your code
    Naming of files- Normal project file naming scheme

  • /Templates(Optional)- If your code requires you to send certain emails/ HTML code to the client-side, store it in this files
    Naming of files- Normal project file naming scheme

  • /Config(Optional)- Configuration files for third party APIs/services like amazon s3 or db connections ...

Files in the root of your project

  • app.js- This file would basically be the entry point of the Express application and should be as minimal as possible
    package.json- file which contains all the project npm details, scripts and dependencies.

  • .gitignore- The files you don’t want to push to git
    Enough talking, let’s see how the project structure would actually look like with some example files

  • .env- This file is where you store all your secret keys, it should never be pushed to your github repo !

That is pretty much it, you are now ready to make your own Express project from scratch !

Find the link to the repo here : Starter project

Latest comments (20)

Collapse
 
aravin profile image
Aravind A

Hi All,

In which folder I have to place the logger (winston)

i.e

src/logger
src/utils

Please suggest

Collapse
 
ezpzdevelopement profile image
Ez Pz Developement

I recommend to read this github.com/goldbergyoni/nodebestpr..., this will guide you and provide a lot of useful information.

I also suggest to change the title to How to structure your next project, because i think that frameworks and programming languages should not control the structure and the architecture of our projects, for example we can follow the same structure/architecture using python , java , c# or go.

and thank you so much for this amazing post

Collapse
 
adhirkirtikar profile image
Adhir Kirtikar

“The .env file should never be pushed to your github repo !”
May I know what is the standard way to use or recreate this env file after we pull the code from GitHub or when the app is deployed in a container?

Collapse
 
nermineslimane profile image
nermineslimane • Edited

Find out all about .env in this article dev.to/nermineslimane/what-is-env-..., concerning the fact that it should never be pushed to version control it's purely and solely for security reasons since it holds info about the secret keys of your app that shouldn't be shared so for that purpose every deployement way has it's specific way to add the .env file , in your case what are you using to deploy the application ?

Collapse
 
adhirkirtikar profile image
Adhir Kirtikar

Thanks for the reply and link.
I used docker to containerize the node.js express app and used GitHub action to add the .env file in the container. I googled this method when I was doing the DEV project. But I think that if someone gets the docker image then they can easily find out the .env file. :D
So I wanted to know what’s the industry standard used to deploy this .env file.

docker-build.yml:

        name: Make envfile
        uses: SpicyPizza/create-envfile@v1
        with:
          envkey_DB_USER: ${{ secrets.DB_USER }}
          envkey_DB_PASS: ${{ secrets.DB_PASS }}
          envkey_DB_NAME: ${{ secrets.DB_NAME }}
          directory: /home/runner/work/IMDb-MongoDb/IMDb-MongoDb
          file_name: .env    ```

Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
nermineslimane profile image
nermineslimane

Looking for the answear for you will you allow to post the subject on a forum ? maybe we'll both find the stardard for this ?

Thread Thread
 
adhirkirtikar profile image
Adhir Kirtikar

Yes of course. 👍🏼

Thread Thread
 
nermineslimane profile image
nermineslimane

wel thank you and here's the link to the stackexchange post if anyone wants to contribute feel free softwareengineering.stackexchange....

Collapse
 
kostyatretyak profile image
Костя Третяк

It is even more useful to divide the logic into modules. Generally speaking, the module should have a set of classes with a narrow specialization. A well-designed module does not have to be a "universal combine".

For example, a security module has a narrow specialization - access security and application management security. Here should not be declared classes which translating messages into different languages, sending mail, writing logs, etc.

When a particular module is tied to a specific URL, it's also good practice, and it can also be considered as a "narrow specialization". For example, one module can process all HTTP requests to /api/users, another module can process /api/posts.

src
└── app
    ├── models
    ├── modules
    │   ├── routed
    │   │   ├── articles
    │   │   │   ├── comments
    │   │   │   └── favorite
    │   │   ├── profiles
    │   │   ├── tags
    │   │   └── users
    │   └── service
    │       ├── app-config
    │       ├── auth
    │       ├── error-handler
    │       ├── logger
    │       ├── msg
    │       ├── mysql
    │       ├── openapi-with-params
    │       ├── util
    │       └── validation
    └── utils
Enter fullscreen mode Exit fullscreen mode
Collapse
 
amrelmohamady profile image
Amr Elmohamady • Edited

It's preferable to put your business logic in a service layer that can access the DAL (data access layer) for loose coupling reasons like:

  • What if you have a Rest API and want to make an MVC version or vice versa use service layer so you don't repeat your logic

  • What if you need to change a third party service like moving from Google cloud storage to AWS S3, use service layer so you don't have to update every function using this service, you will only need to update one file (this service file)

I would use a validation layer before the controller which validates the request body, can be done with something like JOI, express-validator, express-validation, etc..

I would also import & export related layer file from a single entry point like:

controllers/

user.controller.js

index.js
Enter fullscreen mode Exit fullscreen mode

services/

storage.service.js

user.service.js

index.js
Enter fullscreen mode Exit fullscreen mode

and so on for other folders.

So, in index.js, you import other services/controllers and export an object with all services/controllers.

Finally, I suggest reading about dependency injection and its advantages.

Collapse
 
nermineslimane profile image
nermineslimane • Edited

I use this method for the routes, i create one single entry point for all my routes like follows
/routes
-exemple1.routes.js
-exemple2.routes.js
-index.routes.js
-exemple3.routes.js
and in my index.routes.js I export all the routes

Collapse
 
flodev profile image
Florian Klenk

Hi, thanks for sharing. I like the structure.
Just did a quick check how my last express project was structured.
The project was quite complex. I had a routes folder too.
But the exports of each folder was an own router object.

routes/
- user/
- order/
- product/
- checkout/
-- submit-order/
-- checkout.js
- ...
Enter fullscreen mode Exit fullscreen mode

Then in the root of the project dir I had the app.js that put the routers together like so:

appRouter.use('/user', require('./routes/user/user'))
appRouter.use('/checkout', require('./routes/checkout/checkout'))
appRouter.use('/product', require('./routes/product/product'))
appRouter.use('/order', require('./routes/order/order'))
Enter fullscreen mode Exit fullscreen mode

One can say that each routes folder was a project on it's own this way like independent.
Great for microservices if you decide to put it into separate services.

Collapse
 
lowlifearcade profile image
Sonny Brown

Thanks for this. It’s always nice seeing someone share their app structure. It helps me warp my brain around concepts better.

Collapse
 
drsimplegraffiti profile image
Abayomi Ogunnusi

Good post.... What I do sometimes is to wrap the aforementioned folders inside a src folder.

Collapse
 
nermineslimane profile image
nermineslimane

yes most of the time i doo that too, i wrap them inside /app , check the repo here github.com/nermineslimane/Node-exp...

Collapse
 
brentdalling profile image
Brent Dalling • Edited

This is usually a good practice in my opinion. Then, you can add config files, editor configs, etc on the root level of the project. Additionally, you can specify src/ app/ and then storage/ for any user generated files or logs. It just makes everything cleaner.

Collapse
 
nermineslimane profile image
nermineslimane

Check the repo of what i most of the time doo too github.com/nermineslimane/Node-exp...
Feel free to contribute to it if you want too, I prefer tp store user generated file unde /tmp since they're most of the temporary and sometimes i save them under a folder named after the userId or the UserName

Thread Thread
 
dhruvja profile image
Dhruv D Jain

Can I know what ur project is about. All I could find is empty files in routes. If u could specify the agenda we could contribute and improve ur repo

Collapse
 
cheribc profile image
Heather B Cooper

Thank you for the clear examples and explanation for the file structure.

Collapse
 
nermineslimane profile image
nermineslimane

you're welcome, feel free to contact me in case you have question !