Close Menu
Soshace Digital Blog

    Subscribe to Updates

    Get The Latest News, Updates, And Amazing Offers

    What's Hot
    JavaScript

    Build Real-World React Native App #5: Single Post Screen and Bookmark

    JavaScript

    Create simple POS with React.js, Node.js, and MongoDB #16: Order Screen

    JavaScript

    Introduction to Micro Frontends: The New Tech on the Block

    Important Pages:
    • Home
    • About
    • Services
    • Contact Us
    • Privacy Policy
    • Terms & Conditions
    Facebook X (Twitter) Instagram LinkedIn YouTube
    Today's Picks:
    • Scaling Success: Monitoring Indexation of Programmatic SEO Content
    • Leveraging Influencers: Key Drivers in New Product Launches
    • How Privacy-First Marketing Will Transform the Industry Landscape
    • The Impact of Social Proof on Thought Leadership Marketing
    • Balancing Value-Driven Content and Promotional Messaging Strategies
    • Top Influencer Marketing Platforms to Explore in 2025
    • Emerging Trends in Marketing Automation and AI Tools for 2023
    • Strategies to Mitigate Duplicate Content in Programmatic SEO
    Wednesday, September 10
    Facebook X (Twitter) Instagram LinkedIn YouTube
    Soshace Digital Blog
    • Home
    • About
    • Services
    • Contact Us
    • Privacy Policy
    • Terms & Conditions
    Services
    • SaaS & Tech

      Maximizing Efficiency: How SaaS Lowers IT Infrastructure Costs

      August 27, 2025

      Navigating Tomorrow: Innovations Shaping the Future of SaaS

      August 27, 2025

      Maximizing Impact: Strategies for SaaS & Technology Marketing

      August 27, 2025
    • AI & Automation

      Enhancing Customer Feedback Analysis Through AI Innovations

      August 27, 2025

      Navigating the Impact of AI on SEO and Search Rankings

      August 27, 2025

      5 Automation Hacks Every Home Service Business Needs to Know

      May 3, 2025
    • Finance & Fintech

      Critical Missteps in Finance Marketing: What to Avoid

      August 27, 2025

      Analyzing Future Fintech Marketing Trends: Insights Ahead

      August 27, 2025

      Navigating the Complex Landscape of Finance and Fintech Marketing

      August 27, 2025
    • Legal & Compliance

      Exploring Thought Leadership’s Impact on Legal Marketing

      August 27, 2025

      Maximizing LinkedIn: Strategies for Legal and Compliance Marketing

      August 27, 2025

      Why Transparency Matters in Legal Advertising Practices

      August 27, 2025
    • Medical Marketing

      Enhancing Online Reputation Management in Hospitals: A Guide

      August 27, 2025

      Analyzing Emerging Trends in Health and Medical Marketing

      August 27, 2025

      Exploring Innovative Content Ideas for Wellness Blogs and Clinics

      August 27, 2025
    • E-commerce & Retail

      Strategic Seasonal Campaign Concepts for Online and Retail Markets

      August 27, 2025

      Emerging Trends in E-commerce and Retail Marketing Strategies

      August 27, 2025

      Maximizing Revenue: The Advantages of Affiliate Marketing for E-Commerce

      August 27, 2025
    • Influencer & Community

      Leveraging Influencers: Key Drivers in New Product Launches

      August 27, 2025

      Top Influencer Marketing Platforms to Explore in 2025

      August 27, 2025

      Key Strategies for Successful Influencer Partnership Negotiations

      August 27, 2025
    • Content & Leadership

      The Impact of Social Proof on Thought Leadership Marketing

      August 27, 2025

      Balancing Value-Driven Content and Promotional Messaging Strategies

      August 27, 2025

      Analyzing Storytelling’s Impact on Content Marketing Effectiveness

      August 27, 2025
    • SEO & Analytics

      Scaling Success: Monitoring Indexation of Programmatic SEO Content

      August 27, 2025

      Strategies to Mitigate Duplicate Content in Programmatic SEO

      August 27, 2025

      Effective Data Visualization Techniques for SEO Reporting

      August 27, 2025
    • Marketing Trends

      How Privacy-First Marketing Will Transform the Industry Landscape

      August 27, 2025

      Emerging Trends in Marketing Automation and AI Tools for 2023

      August 27, 2025

      Maximizing ROI: Key Trends in Paid Social Advertising

      August 27, 2025
    Soshace Digital Blog
    Blog / JavaScript / Node.js / How to Architect a Node.Js Project from Ground Up?
    JavaScript

    How to Architect a Node.Js Project from Ground Up?

    Shadid HaqueBy Shadid HaqueDecember 19, 2019Updated:December 24, 2019No Comments11 Mins Read
    Facebook Twitter Pinterest Telegram LinkedIn Tumblr Email Reddit
    How to Architect a Node.Js Project from Ground Up?
    Nodejs Architecture
    Share
    Facebook Twitter LinkedIn Pinterest Email Copy Link
    How to architect a Node.js project from ground up?
    How to architect a Node.js project from ground up?

    In this article, we will discuss how to architect a Node.js application properly, and why it is important. Also, we’ll look at what design decisions can lead us to in creating a successful digital product. Maybe you are building a new Node.js application from scratch. Perhaps you would like to refactor your existing application, or perhaps you want to explore Node.js application architecture and learn about the best practices and patterns. Whatever the reason, this article will help you.

    Why should you read this post?

    Well, it is true that there are many blog posts on the internet that cover this very subject. While there are some good articles on architecting Node.js projects, there are none that give you an in-depth explanation. Moreover, there are many blog posts that only elaborate on certain topics (i.e. layered architecture) but don’t tell you how everything fits together in an application. This is why I chose to write this article. I tried to research and compact all the information into one digestible piece so you don’t have to.

    We will briefly go over how to architect a Node.js application properly and discuss the reasoning behind all the design decisions while building an actual dummy application.

    We will discuss

    • Folder structure
    • Configuring environment variables
    • MVC pattern (Model, View, Controller)
    • Layered-architecture
    • Encapsulating Configurations

    We will start with simple concepts and build on them. By the end of this article, you will be able to craft code that you are proud of.

    Excited? Let’s get started!

    Folder Structure

    The organization is important while building large scale projects. We define our folder structure in a way so that it is easy and obvious to find code pieces later. As developers, we often collaborate with others. A well-defined code structure allows us to easily collaborate on a project.

    Below is a sample folder structure that we have been using in my day job and it is working very well for us. We have delivered several successful projects with this structure.  We came up with this after many trials and errors. You are welcome to use this structure or modify it.

    Sample folder structure
    Sample folder structure

    Alright, let’s build our first hello world API endpoint. As we build our sample application we will be populating these folders with code logic.

    First, let’s take a look at our `server.js` file

    const http = require('http');
    const app = require('./app');
    
    const port = process.env.PORT || 3000;
    
    const server = http.createServer(app);
    
    server.listen(port);
    

    Notice that we are requiring our `app.js` file. We will be writing all our app logic in `app.js`. It will be our main entry point for the app. Let’s take a quick look at the code.

    const express = require('express');
    const app = express();
    
    // routes
    app.use((req, res, next) => {
        res.status(200).json({
            message: 'Hello world!!!'
        });
    });
    
    module.exports = app;
    

    For now, we’ve only added a route in our `app.js`. The main reason for separating these two files is to encapsulate logic. Let’s take a look at the npm script that I am using to run this application.

    "scripts": {
        "dev": "nodemon ./src/server.js"
    },
    

    Please do make sure that you are able to run the application by doing `npm run dev`.

    Let’s add resource routes

    I bet you are eager to create some more routes. Let’s do that now. We will be creating the following files in our `api/routes` folder.

    `api/routes/authors.js`

    `api/routes/books.js`

    Let’s just return some dummy JSON data from these routes.

    /**
     * GET request to /books
     */
    router.get('/', (req, res, next) => {
        res.status(200).json({
            message: 'All Books were fetched'
        });
    });
    
    /**
     * GET request to /books/:id
     */
    router.get('/:id', (req, res, next) => {
        res.status(200).json({
            message: 'Book with id was fetch'
        });
    });

    You can do something similar for the author routes as well for now. Later in the post we will be discussing separation of concerns, and how we can architect our application with model view controller pattern. Before we do that, let’s cover one other important topic, setting up environment variables.

    Configuring our environment variables

    As programmers, we often underestimate the importance of organizing and configuring environment variables.  It is important that our apps work in various environments. This could be your colleagues’ computer, in a server, in a docker container, or in some other cloud provider. Therefore, setting up environment variables is crucial while architecting a Node.js application.

    Read More:  23. Node.js Lessons. Domains, asynchronous try.. catch. Part 3.

    I am using `dotenv` library to manage environment variables in this application. First, I installed the library with `npm i install dotenv –save`. Then I created a  .envfile in the root directory. We add all of our environment variables in this `.env` file. Below is my sample `.env` setup.

    PORT=3000
    API_URL=https://api.some/endpoint
    API_KEY=kkaskdwoopapsdowo
    MONGO_URL=

    It is a good practice to gather our variables from `.env` file and map them into well-named variables and export them through a module. Let’s create a file `config/index.js`.

    const dotenv = require('dotenv');
    dotenv.config();
    module.exports = {
      endpoint: process.env.API_URL,
      masterKey: process.env.API_KEY,
      port: process.env.PORT
    };
    

    The main reason for doing this is to manage our environment variables in one place. For some reason, we may decide to have multiple `.env` files. For instance, we may decide to have a separate `.env` for deployment with docker. We may also have other configuration variables. We would like to manage these variables efficiently that’s why we are following this convention.

    Alright, now let’s see how we can import these variables into `server.js`

    const http = require('http');
    const app = require('./app');
    const { port } = require('./config');
     
    const server = http.createServer(app);
     
    server.listen(port);

    We have set up our environment variables. Let’s dive into the model-view-controller pattern now.

    Model-View-Controller Pattern

    MVC
    MVC

    Modern web applications are big and complex. To reduce complexity we use the Separation of responsibility principle (SRP). Using SRP ensures loose coupling, maintainability, and testability. MVC pattern embodies this philosophy of separation of responsibility. Let’s take a look at the different parts of MVC.

    Model: 

    Model components are responsible for application’s data domain. Model objects are responsible for storing, retrieving, and updating data from the database.

    View: 

    It is the user interface of our application. In most modern web applications, the view layer is usually replaced by another single page application, for example, a React.js or an Angular application.

    Controllers:

    They are responsible for handling user interaction. They interact with models to retrieve information and ultimately respond to user requests. In smaller applications, controllers can hold business logic. However, it is not good practice for larger application; we will look into a layered architecture later in this article to further elaborate on why this is.

    Now, let’s take a look at how we can add this pattern to our application. I will be using `mongodb` as our database for this demo. I have created a new controller and a model to implement this pattern. First, let’s take a look at the `author` model.

    const mongoose = require('mongoose');
    const authorSchema = mongoose.Schema({
        _id: mongoose.Schema.Types.ObjectId,
        name: { type: String, required: true },
        books: { type: Object, required: false }
    });
    module.exports = mongoose.model('Author', authorSchema);
    

    We are defining our database-related schemas in the model as well. The controllers will deal with all the fetching and business logic for now. So let’s take a look at the controller.

    module.exports = {
        createAuthor: async (name) => {
            const author = new Author({
                _id: new mongoose.Types.ObjectId(),
                name: name
            });
            try {
                const newAuthorEntry = await author.save()
                return newAuthorEntry; 
            } catch (error) {
                throw error
            }
        },
    
        getAuthor: async (id) => {
            // ..
        },
    
        getAllAuthors: async() => {
            // ...
        }
    }
    

    Now we can slim down our router as follows:

    /**
     * POST create /author
     */
    router.post("/", async (req, res, next) => {
        const author = await authorController.createAuthor(req.body.name)
        res.status(201).json({
            message: "Created successfully",
            author
        })
    });
    

    Using this pattern separates our concerns and keeps the code clean, organized and testable. Our components are now following the single responsibility principle. For instance, our routes are only responsible for returning a response; controllers handle most of the business logic and models take care of the data layer.

    Note: To get the code up to this point please check the following github repo:

    https://github.com/Shadid12/starter_node/tree/part-2

    Let’s say our business requirement has changed. Now, when we are adding a new author, we have to check if they have any best selling titles and whether the author is self-published or he/she belongs to a certain publication. So now if we start implementing this logic in our controllers things, begin to look rather messy.

    Looks at the code below, for instance:

    createAuthor: async (name) => {
            const author = new Author({
                _id: new mongoose.Types.ObjectId(),
                name: name
            });
            try {
                // cehck if author is best-seller
                const isBestSeller = await axios.get('some_third_part_url');
                // if best seller do we have that book in our store 
                if(isBestSeller) {
                    // Run Additional Database query to figure our
                    //...
                    //if not send library admin and email 
                    //...
                    // other logic and such
                }
                const newAuthorEntry = await author.save()
                return newAuthorEntry; 
            } catch (error) {
                throw error
            }
    },
    

    Now, this controller becomes responsible for doing multiple actions, this makes it harder to test, messy, and it is breaking the Single Responsibility Principle. 

    Read More:  Securing Node.js Applications with JWT and Passport.js

    How do we solve this problem? With the layered architecture!

    Layered Architecture for Node.js

    We want to apply the separation of concerns principle and move our business logic away from our controllers. We will create small service functions that will be called from our controllers. These services are responsible for doing one thing only, so in this way, our business logic is encapsulated. That way, if, in the future, requirements changes, we will only need to change certain service functions, and it will prevent any domino effects. With layered architecture, we build applications that are agile and allow changes to be introduced very easily when necessary. This architecture is also referred to as a 3-layer-architecture.

    Here’s a visual breakdown of what we are about to do:

    3-layer-architecture
    3-layer-architecture

    Alright so let’s break down our previous controller to use this architecture.  To start, we will need to create services to handle specific events.

    createAuthor: async (name) => {
            const author = new Author({
                _id: new mongoose.Types.ObjectId(),
                name: name
            });
            try {
                await AuthorService.checkauthorSalesStatus();
                await BookService.checkAvailableBooksByAuthor(name);
                const newAuthorEntry = await author.save();
                return newAuthorEntry; 
            } catch (error) {
                throw error
            }
    },
    

    Notice that service functions are designed to do one specific task. This way, our services are encapsulated, testable, and open to future changes without any major side effects.

    Encapsulating Configurations

    We write a fair amount of configuration code in our Node.js application. These usually run when the application boots up. It is good practice to have these encapsulated inside a function. This will allow us to track these files better and debug them if necessary.

    Let’s elaborate on this with an example. Below we have our `app.js` file

    const express = require('express');
    const app = express();
    const mongoose = require('mongoose');
    const { mongoUrl } = require('./config');
    const bodyParser = require('body-parser');
     
    //routes 
    const authorsRoutes = require('./api/routes/authors');
    const booksRoutes = require('./api/routes/books');
     
    mongoose.connect(mongoUrl, { useNewUrlParser: true });
    mongoose.Promise = global.Promise;
     
    app.use(bodyParser.urlencoded({ extended: false }));
    app.use(bodyParser.json());
     
    app.use((req, res, next) => {
        res.header("Access-Control-Allow-Origin", "*");
        res.header(
          "Access-Control-Allow-Headers",
          "Origin, X-Requested-With, Content-Type, Accept, Authorization"
        );
        if (req.method === "OPTIONS") {
          res.header("Access-Control-Allow-Methods", "PUT, POST, PATCH, DELETE, GET");
          return res.status(200).json({});
        }
        next();
    });
     
    app.use('/authors', authorsRoutes);
    app.use('/books', booksRoutes);
     
    module.exports = app;

    We have a couple of things that are just configuration code. For instance, database connection, body parser, and cors setup are all server configuration code. We can move them into their own separate functions inside `config` folder.

    const mongoose = require('mongoose');
    const { mongoUrl } = require('./index');
     
    module.exports = {
        initializeDB: async () => {
            mongoose.connect(mongoUrl, { useNewUrlParser: true });
            mongoose.Promise = global.Promise;
        },
     
        cors: async (req, res, next) => {
            res.header("Access-Control-Allow-Origin", "*");
            res.header(
            "Access-Control-Allow-Headers",
            "Origin, X-Requested-With, Content-Type, Accept, Authorization"
            );
            if (req.method === "OPTIONS") {
            res.header("Access-Control-Allow-Methods", "PUT, POST, PATCH, DELETE, GET");
            return res.status(200).json({});
            }
            next();
        }
    }
    

    And now we can use those functions in our `app.js`

    const express = require('express');
    const app = express();
    const bodyParser = require('body-parser');
    const config = require('./config/init')
     
    //routes 
    const authorsRoutes = require('./api/routes/authors');
    const booksRoutes = require('./api/routes/books');
     
     
    app.use(bodyParser.urlencoded({ extended: false }));
    app.use(bodyParser.json());
     
    app.use(config.cors);
     
    app.use('/authors', authorsRoutes);
    app.use('/books', booksRoutes);
     
    module.exports = app;
    

    And that’s it. Our `app.js` is now looking much cleaner.

    Finally, here are the key points to keep in mind for a Node.js project architecture:

    1. Apply proper folder structure: It allows us to easily locate files and code. Also enables better collaboration with the team;
    2. Configuring environment variables: Configure and manage environment variables properly to avoid deployment;
    3. MVC pattern (Model, View, Controller): Apply MVC pattern to decouple, testable, and maintainable code;
    4. Layered Architecture: Apply layered architecture to separate your concerns. Use services extensively to encapsulate your business logic;
    5. Encapsulating Configurations: Separate configuration code from application logic.

    We briefly went over the core concepts of Node.js project architecture. I hope this article was helpful to you and gave you some insights on how to architect your own project. I would love to hear what you think about this blog post. Please share your thoughts in the comment, if you enjoyed reading this please like and share. Until next time!

    JavaScript Node.js programming web application web-development
    Share. Facebook Twitter Pinterest LinkedIn Tumblr Email
    Shadid Haque

      Related Posts

      Mastering REST APIs: Essential Techniques for Programmers

      December 18, 2024

      Streamlining Resource Allocation for Enhanced Project Success

      December 18, 2024

      Crafting Interactive User Interfaces Using JavaScript Techniques

      December 17, 2024
      Leave A Reply Cancel Reply

      You must be logged in to post a comment.

      Stay In Touch
      • Facebook
      • Twitter
      • Pinterest
      • Instagram
      • YouTube
      • Vimeo
      Don't Miss
      JavaScript June 19, 2019

      Introduction to the Best Code Playgrounds: JSFiddle, Codepen, and CodeSandbox

      With the advancements in JavaScript in the latest years, creating, editing, and running code is no longer constrained to offline editors running on the developer’s computer; nowadays, we can run our code in online environments called “playgrounds” — and there’s quite a lot of advantages of using them.

      Analyzing Future Fintech Marketing Trends: Insights Ahead

      August 27, 2025

      React vs. Angular: Choosing The Right Tools for Your Next Project

      April 4, 2019

      Create simple POS with React.js, Node.js, and MongoDB #7: Adding redux to other components

      June 22, 2020

      Categories

      • AI & Automation
      • Angular
      • ASP.NET
      • AWS
      • B2B Leads
      • Beginners
      • Blogs
      • Business Growth
      • Case Studies
      • Comics
      • Consultation
      • Content & Leadership
      • CSS
      • Development
      • Django
      • E-commerce & Retail
      • Entrepreneurs
      • Entrepreneurship
      • Events
      • Express.js
      • Facebook Ads
      • Finance & Fintech
      • Flask
      • Flutter
      • Franchising
      • Funnel Strategy
      • Git
      • GraphQL
      • Home Services Marketing
      • Influencer & Community
      • Interview
      • Java
      • Java Spring
      • JavaScript
      • Job
      • Laravel
      • Lead Generation
      • Legal & Compliance
      • LinkedIn
      • Machine Learning
      • Marketing Trends
      • Medical Marketing
      • MSP Lead Generation
      • MSP Marketing
      • NestJS
      • Next.js
      • Node.js
      • Node.js Lessons
      • Paid Advertising
      • PHP
      • Podcasts
      • POS Tutorial
      • Programming
      • Programming
      • Python
      • React
      • React Lessons
      • React Native
      • React Native Lessons
      • Recruitment
      • Remote Job
      • SaaS & Tech
      • SEO & Analytics
      • Soshace
      • Startups
      • Swarm Intelligence
      • Tips
      • Trends
      • Vue
      • Wiki
      • WordPress
      Top Posts

      Ultimate Reading List for Developers | 40 Web Development Books

      JavaScript July 22, 2019

      Enhancing Software Development Efficiency with Agile Methodology

      Development November 24, 2024

      Strategic LinkedIn Branding: A Key to Effective Lead Generation

      LinkedIn December 9, 2024

      The Best Work Tools for Remote Teams — Part 2: Team Communication

      Job May 3, 2019

      Subscribe to Updates

      Get The Latest News, Updates, And Amazing Offers

      About Us
      About Us

      Soshace Digital delivers comprehensive web design and development solutions tailored to your business objectives. Your website will be meticulously designed and developed by our team of seasoned professionals, who combine creative expertise with technical excellence to transform your vision into a high-impact, user-centric digital experience that elevates your brand and drives measurable results.

      7901 4th St N, Suite 28690
      Saint Petersburg, FL 33702-4305
      Phone: 1(877)SOSHACE

      Facebook X (Twitter) Instagram Pinterest YouTube LinkedIn
      Our Picks
      JavaScript

      Node.js Lesson 8: Inheritance from Errors, Error

      Entrepreneurs

      Mastering Negotiation: Essential Strategies for Entrepreneurs

      JavaScript

      Using SWR for Efficient Data Fetching in Next.js Applications

      Most Popular

      Transforming LinkedIn Connections into Effective Sales Leads

      LinkedIn

      Java Stream API

      Beginners

      Node.js Lesson 8: Inheritance from Errors, Error

      JavaScript
      © 2025 Soshace Digital.
      • Home
      • About
      • Services
      • Contact Us
      • Privacy Policy
      • Terms & Conditions

      Type above and press Enter to search. Press Esc to cancel.