How to Structure Your Backend Code in Node.js (Express.js)
Vishal Yadav

Vishal Yadav @vyan

About: I Post Daily Blogs About Full-Stack Development

Location:
India
Joined:
May 3, 2024

How to Structure Your Backend Code in Node.js (Express.js)

Publish Date: Jul 17 '24
717 73

When developing a Node.js application using Express.js, structuring your codebase effectively is crucial for maintainability, scalability, and ease of collaboration. A well-organized project structure allows you to manage complexity, making it easier to navigate and understand the code. In this blog, we'll explore a typical folder structure for an Express.js application and explain the purpose of each directory and file.

Project Structure Overview

Here’s a common folder structure for an Express.js application:

📁
├── 📄 app.js
├── 📁 bin
├── 📁 config
├── 📁 controllers
│   ├── 📄 customer.js
│   ├── 📄 product.js
│   └── ...
├── 📁 middleware
│   ├── 📄 auth.js
│   ├── 📄 logger.js
│   └── ...
├── 📁 models
│   ├── 📄 customer.js
│   ├── 📄 product.js
│   └── ...
├── 📁 routes
│   ├── 📄 api.js
│   ├── 📄 auth.js
│   └── ...
├── 📁 public
│   ├── 📁 css
│   ├── 📁 js
│   ├── 📁 images
│   └── ...
├── 📁 views
│   ├── 📄 index.ejs
│   ├── 📄 product.ejs
│   └── ...
├── 📁 tests
│   ├── 📁 unit
│   ├── 📁 integration
│   ├── 📁 e2e
│   └── ...
├── 📁 utils
│   ├── 📄 validation.js
│   ├── 📄 helpers.js
│   └── ...
└── 📁 node_modules
Enter fullscreen mode Exit fullscreen mode

Explanation of Each Directory and File

app.js

The app.js file is the entry point of your application. It’s where you initialize the Express app, set up middleware, define routes, and start the server. Think of it as the control center of your web application.

const express = require('express');
const app = express();
const config = require('./config');
const routes = require('./routes');

// Middleware setup
app.use(express.json());

// Routes setup
app.use('/api', routes);

// Start server
const PORT = config.port || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

module.exports = app;
Enter fullscreen mode Exit fullscreen mode

bin

The bin directory typically contains scripts for starting your web server. These scripts can be used to set environment variables or manage different environments (e.g., development, production).

Example: bin/www

#!/usr/bin/env node

const app = require('../app');
const debug = require('debug')('your-app:server');
const http = require('http');

const port = normalizePort(process.env.PORT || '3000');
app.set('port', port);

const server = http.createServer(app);
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

function normalizePort(val) {
  const port = parseInt(val, 10);
  if (isNaN(port)) return val;
  if (port >= 0) return port;
  return false;
}

function onError(error) {
  if (error.syscall !== 'listen') throw error;
  const bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;
  switch (error.code) {
    case 'EACCES':
      console.error(bind + ' requires elevated privileges');
      process.exit(1);
      break;
    case 'EADDRINUSE':
      console.error(bind + ' is already in use');
      process.exit(1);
      break;
    default:
      throw error;
  }
}

function onListening() {
  const addr = server.address();
  const bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
  debug('Listening on ' + bind);
}
Enter fullscreen mode Exit fullscreen mode

config

The config directory holds configuration files for your application, such as database connections, server settings, and environment variables.

Example: config/index.js

module.exports = {
  port: process.env.PORT || 3000,
  db: {
    host: 'localhost',
    port: 27017,
    name: 'mydatabase'
  }
};
Enter fullscreen mode Exit fullscreen mode

controllers

Controllers contain the logic for handling incoming requests and generating responses. Each file in the controllers directory typically corresponds to a different part of your application (e.g., customers, products).

Example: controllers/customer.js

const Customer = require('../models/customer');

exports.getAllCustomers = async (req, res) => {
  try {
    const customers = await Customer.find();
    res.json(customers);
  } catch (err) {
    res.status(500).json({ message: err.message });
  }
};
Enter fullscreen mode Exit fullscreen mode

middleware

Middleware functions are used to process requests before they reach the controllers. They can handle tasks like authentication, logging, and request validation.

Example: middleware/auth.js

module.exports = (req, res, next) => {
  const token = req.header('Authorization');
  if (!token) return res.status(401).json({ message: 'Access Denied' });

  try {
    const verified = jwt.verify(token, process.env.JWT_SECRET);
    req.user = verified;
    next();
  } catch (err) {
    res.status(400).json({ message: 'Invalid Token' });
  }
};
Enter fullscreen mode Exit fullscreen mode

models

Models define the structure of your data and handle interactions with the database. Each model file typically corresponds to a different data entity (e.g., Customer, Product).

Example: models/customer.js

const mongoose = require('mongoose');

const customerSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true
  },
  email: {
    type: String,
    required: true,
    unique: true
  },
  createdAt: {
    type: Date,
    default: Date.now
  }
});

module.exports = mongoose.model('Customer', customerSchema);
Enter fullscreen mode Exit fullscreen mode

routes

Routes define the paths to different parts of your application and map them to the appropriate controllers.

Example: routes/api.js

const express = require('express');
const router = express.Router();
const customerController = require('../controllers/customer');

router.get('/customers', customerController.getAllCustomers);

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

public

The public directory contains static files like CSS, JavaScript, and images that are served directly to the client.

Example: Directory Structure

public/
├── css/
├── js/
├── images/
Enter fullscreen mode Exit fullscreen mode

views

Views are templates that render the HTML for the client. Using a templating engine like EJS, Pug, or Handlebars, you can generate dynamic HTML.

Example: views/index.ejs

<!DOCTYPE html>
<html>
<head>
  <title>My App</title>
  <link rel="stylesheet" href="/css/styles.css">
</head>
<body>
  <h1>Welcome to My App</h1>
  <div id="content">
    <%- content %>
  </div>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

tests

The tests directory contains test files to ensure your application works correctly. Tests are often organized into unit tests, integration tests, and end-to-end (e2e) tests.

Example: Directory Structure

tests/
├── unit/
├── integration/
├── e2e/
Enter fullscreen mode Exit fullscreen mode

utils

Utility functions and helper modules are stored in the utils directory. These functions perform common tasks like validation and formatting that are used throughout the application.

Example: utils/validation.js

exports.isEmailValid = (email) => {
  const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return re.test(String(email).toLowerCase());
};
Enter fullscreen mode Exit fullscreen mode

node_modules

The node_modules directory contains all the dependencies your project needs. This directory is managed by npm (or yarn) and includes packages installed from the npm registry.

Conclusion

A well-structured Node.js application using Express.js enhances maintainability, scalability, and collaboration. Each directory and file in the structure serves a specific purpose, from handling configuration and defining routes to managing middleware and rendering views. By organizing your codebase effectively, you can build robust and scalable applications with ease.

Comments 73 total

  • Florian Rappl
    Florian RapplJul 17, 2024

    While the micro scheme is fine, it lacks a proper repository structuring.

    As an example, not aggregating the source code in a src directory is a huge misstep. You might want to use TypeScript or some other tooling (e.g., a bundler) to process your code for performance benefits. As such putting everything into the root / mixing with your tooling and tests is something you want to avoid strongly.

    (You should not use CommonJS - use ESModules; especially if you really care about "maintainability, scalability, and collaboration".)

    • Adesoji1
      Adesoji1Jul 17, 2024

      nice work, thank you for this, for a react frontend that has typescript in it, how should the folder structure be arranged?

      • Florian Rappl
        Florian RapplJul 17, 2024

        Just put everything in a source and write the code as-is. (e.g., then src/index.tsx instead of a index.js or src/index.js). Use a tool such as esbuild to consume the source files and transform (+ bundle) them into a dist folder (src/index.tsx and related files -> dist/app.js).

        It's a standard practice that pretty much all applications today follow.

      • Vishal Yadav
        Vishal YadavJul 18, 2024

        I will share soon!

    • Adesoji1
      Adesoji1Jul 17, 2024

      if i want to convert the javascript application in ES module to a desktop application using electronjs, how do i do it?

      • Florian Rappl
        Florian RapplJul 17, 2024

        Electron.js understands ESM (electronjs.org/docs/latest/tutoria...) - but if you want to only use ESM in development and another format (e.g., CommonJS) at runtime I recommend using a tool such as esbuild (which can bundle and transform).

      • Vishal Yadav
        Vishal YadavJul 18, 2024

        using tools like Electron Packager or Electron Builder.

    • Clarence Liu
      Clarence LiuJul 18, 2024

      I agree, this is fine for JS, but any serious project should really be using Typescript as you said, type-safe SDKs are super powerful, see Tanstack Router or others that are built with this in mind.

    • Juan José Cruz Ortiz
      Juan José Cruz OrtizJul 22, 2024

      you are wrong since the moment you want to put types to a non typed language, if want types go and do you stuff in c# or java

      • Florian Rappl
        Florian RapplJul 22, 2024

        My comment was independent if you like or want to use TypeScript. That was one example. Having a src directory is pretty much always helpful (as you might want to bundle for performance reasons).

        Maybe instead of making useless comments it would be great to improve your knowledge about JS and its ecosystem from time to time. Might help.

        • Juan José Cruz Ortiz
          Juan José Cruz OrtizJul 22, 2024

          If you had a good understanding of js you wouldn't have to use typescript in the first place, and secondly you've obviously never worked on any big enough project where the ShitTypescript spends 2 hours making bundles for perforance reasons at every line change

          • NguyenDat251
            NguyenDat251Aug 9, 2024

            Working on a big project without needing Typescript? You are the one who sounds like you have never worked on any big one. That is Typescript's purpose: to add typing syntax for js, to let the code document itself. So when the project is scaled up, people can join and easily follow up the codebase.

            • Vishal Yadav
              Vishal YadavAug 9, 2024

              Yeah , you are right , I'm solo freelancer! but it will help who is new in this field.

    • Ashish Shukla
      Ashish ShuklaJul 28, 2024

      That’s well said but if we are really aiming that big and complex applications, then we should use another frameworks like nestjs which provide all these things out of the box.

  • Omara Patrick
    Omara PatrickJul 17, 2024

    The code looks well organized and readable

  • Shemika Donalene
    Shemika DonaleneJul 18, 2024

    Thank you for this helpful overview on structuring Node.js/Express.js backend code. The folder structure and organization principles you've outlined are valuable for creating maintainable and scalable applications.

    I'd like to mention how tools like Servbay can complement and enhance this type of structured backend architecture. Servbay is designed to streamline the development of server-side applications, which aligns well with the goals of proper code organization.

    Some ways Servbay could potentially integrate with this structure:
    API Routes: Servbay could help generate or manage the routes defined in the routes directory, potentially automating some of the boilerplate code.
    Controllers: The tool might offer features to scaffold or enhance controller logic, working alongside the manually created controllers in the controllers directory.
    Models: Servbay's data modeling capabilities could integrate with or supplement the Mongoose models defined in the models folder.

  • Izuorah Dubem kizito
    Izuorah Dubem kizitoJul 18, 2024

    I prefer modular setup. Makes it easier to scale up or down by attaching or detaching modules

  • Srishti Prasad
    Srishti PrasadJul 18, 2024

    Nice insights!! @vyan

  • Yogesh Joshi
    Yogesh JoshiJul 18, 2024

    I request to you if possible then create GitHub repo for us. that more help to beginner like me.

  • Wojtek
    WojtekJul 18, 2024

    DDD hex

  • Bhaskar Ghale
    Bhaskar GhaleJul 18, 2024

    It might have been better to dive into one of the popular start kits and explore why they are structured that way.
    But this is a good one nonetheless!

  • Aima Smith
    Aima SmithJul 18, 2024

    Best and informative

  • Mahmoud_Walid
    Mahmoud_WalidJul 18, 2024

    Oh, Thanks, This is what I was waiting for ⭐

  • Biparker
    BiparkerJul 18, 2024

    Thanks for the guide, as a beginner ejs coder, I deployed my first website to be used by my coding learner group. Will help rewrite and organize.

  • Divyang Chhantbar
    Divyang ChhantbarJul 18, 2024

    Thanks for posting Vishal , infact i am also following somewhat like this structure only and believe me it helps a lot and make it less complex for the devs to understand stuff.

  • Martin Baun
    Martin BaunJul 18, 2024

    Thank you very much for another insightful and important article!

  • programORdie
    programORdieJul 18, 2024

    Very usefull! Thank you for writing this

  • Prashasth Nair
    Prashasth NairJul 19, 2024

    Nice Explaination.

  • BENIKAD
    BENIKADJul 19, 2024

    Nice work here

  • Jason V. Castellano
    Jason V. CastellanoJul 19, 2024

    So far so good (-:. You can add repository later for your queries and create entities for your business logic.

  • Ronaldo Modesto
    Ronaldo ModestoJul 19, 2024

    Very good article 💯. Structuring projects is a more difficult task than it seems because we have to think about possible developments and how they can be accommodated in the least painful way possible.
    If you allow me, I would just like to make a suggestion for improvement.
    The regex used to validate email is vulnerable to ReDos (Regex Denial Of Service). It would be interesting to perhaps try to replace it with another one in the article so that other people who come to read the article don't end up using this regex to validate data in real systems.
    Here's a tip for readers, always validate your regex before using.
    I generally use this site to check if a regex is vulnerable: devina.io/redos-checker
    Once again congratulations on the article 😃

  • Ayush Kumar Shaw
    Ayush Kumar ShawJul 19, 2024

    Thanks for this amazing writeup, Vishal. ❤️ed it!

    It'll be great if you can share a sample GitHub repo for referencing a similar project structure. Would be a great addition as a reference material.

    That said, I recently came up with a backend microservice template using JavaScript, Node.js, and Express.js. Although incomplete, it's quite practical and can be used to build reliable backend services. In my template though I've taken inspiration from NestJS for their modular approach to the directory structure.

    Your blog though is a great reference for me to build another template, one that corresponds to the more common way of organizing the project, the way you've portrayed here.

    So again, thanks! 💯

  • Iyere Handsome(Beau) Omogbeme
    Iyere Handsome(Beau) OmogbemeJul 19, 2024

    Not going to lie this directory structure is archaic and for beginners who are new to expressjs. There is are far more approachable and preferable ways to structure an expressjs app, most especially when working with typescript at the backend.

  • Amin Jalal
    Amin JalalJul 20, 2024

    hi

  • OssiDev
    OssiDevJul 20, 2024

    I have always had a bit of a problem with this type of folder structure. I don't understand how you can modularize your code with it. You have, at the root of source directory, the typical controllers, services, repositories (application, domain and data layers) folders and inside those you have similarly named files and classes, like "customer" or "product". While the controller, the service and the repository for "customer" belong together, they aren't close to each other, so it's harder to make that connection.

    I always considered it better to have a modules/ folder inside which I have the customer and product and inside those folders have layers like controllers/, services/, views/ and persistence/ or repositories/ etc. This way I can easily focus on working inside one module, and if I need to, I can easily separate it to it's own service.

    A tip there is to use DDD, so that you create modules based on the bounded contexts and you can easily create microservices based on those bounded contexts. Yeah, you will have more nested folders, but you still have a more clear grouping and separating them later (if needed) is much less painful.

  • mitesh bhagwant
    mitesh bhagwantJul 21, 2024

    Please share GitHub repo.

  • Chibueze Onyekpere
    Chibueze OnyekpereJul 21, 2024

    Thank you for this. I believe well structured code will save me from a lot of headache

  • Chibueze Onyekpere
    Chibueze OnyekpereJul 21, 2024

    I really like this post. Let's connect

    • Vishal Yadav
      Vishal YadavAug 9, 2024

      Yes , you can see my social handle on my profile!

  • Dennis kinuthia
    Dennis kinuthiaJul 22, 2024

    i've been preferring grouping related items to the point where i can copy the posts route directory into another project and it would mostly work

  • raselldev
    raselldevJul 23, 2024

    better using per services area

  • liansun96
    liansun96Jul 26, 2024

    Yoooooo

  • Aditya Pratama
    Aditya PratamaAug 6, 2024

    if I use express generator do I have to put it in the src folder?

  • Aditya Pratama
    Aditya PratamaAug 7, 2024

    why don't you put it in the src folder

    • Vishal Yadav
      Vishal YadavAug 9, 2024

      Because , I see most people use this way only, You can put your code in src , it will be more structured.

  • Sina HeydariNejad
    Sina HeydariNejadAug 12, 2024

    thanks for all the information :) huge hug from Iran .

  • James Oduor
    James OduorAug 22, 2024

    Good job this is well done. I think for me as a beginner it's the principle. whichever approach one choses it must satisfy the principle of separation of concerns. For those criticizing I think it would be best to support your claim with a well organized and concise alternative as this presented herein rather than hate and throw things here and there. Once again good job @vyan 🇰🇪

  • Pál András Ciocan (Andris)
    Pál András Ciocan (Andris)Sep 30, 2024

    Well, i came across this article through daily.dev and gosh it helps me a lot, because i'm in the middle of structuring my backend in an important project. I tried structuring based on this -> github.com/goldbergyoni/nodebestpr...
    Now i realize, @vyan article is what i need to go on.
    Thanks for sharing.

  • krishna-Tiwari
    krishna-TiwariOct 6, 2024

    I think this is fine also but better folder structure might be module based. This is easier to maintain and structure.

  • Aayush Goyal
    Aayush GoyalJan 20, 2025

    Good article for beginners.

Add comment