How to Build A Well Structured 3-tier Architecture (MERN Stack - ES6) [Step-by-Step Guide]

Written by calvinqc | Published 2019/10/26
Tech Story Tags: javascript | nodejs | mongodb | express | scalability | eslint | latest-tech-stories

TLDR This project serves as a guide/template for a 3-tier architecture. Built a simple/good structured SERVER App with NodeJS/ExpressJS/MongoDB built a ReactJS app and integrate it with your SERVER from (1) Postman: this will allow you to test your API (GET, POST, PUT, DELETE, etc.) Express Middleware: This is an essential framework for NodeJS to start a server project. Babel: This compiles ES6 to ES5 to compress the project size.via the TL;DR App

This project serves as a guide/template for a 3-tier architecture. If you are entirely new to Web Development. I highly suggest you watch this video first.
This project contains 2 parts:
  1. Built a simple/good structured SERVER App with NodeJS/ExpressJS/MongoDB
  2. Built a ReactJS app and integrate it with your SERVER from (1).
In this post, I will show you the first part to build your Server API.

Requirement

  1. Node.js
  2. npm registry
  3. Text Editor: VSCode OR …anything you like.
  4. Postman: this will allow you to test your API (GET, POST, PUT, DELETE, etc.)
Getting Started
Open your Terminal and redirect to the directory where you want to place your project:
$ mkdir project && mkdir project/server && mkdir project/client && cd server/
Now, you’re in the Server folder. Create a file package.json and add the below code.
$ touch package.json
This is where you store all the project dependencies, and scripts to start your application
{
   "name": "server",
   "version": "1.0.0",
   "private": true,
   "scripts": {
   "start": "node -r esm app.js",
   "dev": "nodemon -r esm app.js"
 },
}

Install

1. ESLint Airbnb
Install ESLint Airbnb to allow all developers to have the same coding style and follow a right Javascript coding style
$ npx install-peerdeps — dev eslint-config-airbnb
Create .eslintrc in your project and add this:
{
   "extends": "airbnb"
}
2. Babel
Install Babel: This compiles ES6 to ES5 to compress the project size when pushing to production to reduce run-time and because many web browsers can only read ES5.
$ npm install esm babel-cli babel-register babel-preset-es2015 babel-preset-stage-2 babel-core babel-polyfill — save-dev
Create .babelrc file in your server project and add this:
{
   "presets": ["es2015", "stage-2"]
}
3. Express Middleware
Install the first 3 middlewares to run your App:
$ npm i — save express esm nodemon
Express: an essential framework for NodeJS to start a server project.
esm: This goes with `babel` and allows you to run ES6.
nodemon: This is my favorite; it will enable you to restart the server automatically whenever you make changes in the server.

Build your Server

The first step is to create a file that will contain our code for Node.js Server
$ touch app.js
This app.js will start a server on PORT 8080 and initialize all the dependencies that your app requires. Add this simple code to app.js
// Import all dependencies & middleware here
import express from 'express';

// Init an Express App. This later starts a server and put all dependencies into your project to use
const app = express();

// Use your dependencies here

// use all controllers(APIs) here
app.get('/', (req, res) => {
   res.status(200).json({
      status: 'success'
   });
});

// Start Anything here
app.listen(8080, () => {
   console.log('Example app listening on port 8080!');
});

Start your Server

You can find the script that runs these functions in package.json
$ npm start
OR (To run automatically whenever you make a new change, run used by nodemon)
$ npm run dev

Use Dependencies/Middleware

Express is a framework, but it doesn’t mean that it contains all you need to make a great web app. Then, you’ll need to import more powerful libraries.
An example: Install body-parser:
$ npm i body-parser
Import this to app.js:
import bodyParser from 'body-parser';

// Use your dependencies here
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

Create RESTful APIs

Now, your project is getting complicated, and you don’t want to put all your API into app.js, which is used only for starting & initializing your app.
You want to separate your APIs into different folders.
Run the following commands:
$ mkdir controller 
$ touch controller/index.js && touch controller/user.controller.js
Open your user.controller.js and import this code in there:
import express from 'express';
const userController = express.Router();
userController.get('/', (req, res) => {
   res.status(200).json({
      status: 'success'
   });
});
export default userController;
Express Router is a class which helps us to create router handlers. It also can extend this routing to handle validation, handle 404 or other errors, etc.

Scalability

Assume your project has many controllers. You don’t want to keep importing all controllers to your app.js. Then, you want to use 1 file to import all controllers.
Open index.js in your controller and import this:
import userController from './user.controller';
//import abcController from './abc.controller';
//import xyzController from './xyz.controller';
export {
   userController,
   //abcController,
   //xyzController
};
Right now, you only have userController, but if you have more controllers, un-comment the import and export.
NOTE: the comments are just examples.

Adding API to Express App

You just created the Controller, but you haven’t told Express App to use it.
In app.js, first import Controllers:
import { 
   userController,
} from './controller';
Replace this:
app.get('/', (req, res) => {
   res.status(200).json({
      status: 'success'
   });
});
with this:
app.use('/', userController);
// Uncomment and modify the route if you want to use any controllers
//app.use('/abc', abcController);
//app.use('/xyz', xyzController);

Database

You can choose any Database Language to learn and apply. In this project, I will use MongoDB as it has an excellent library to interact with NodeJS.
Install & Start MongoDB
You will need to install Mongoose: “Mongoose provides a straight-forward, schema-based solution to model your application data.”
Open a new terminal:
$ brew update
$ brew tap mongodb/brew
$ brew install mongodb-community@4.2
Open your previous terminal:
$ npm i mongoose
Connection
In your app.js, import mongoose:
import bodyParser from 'body-parser';
import mongoose from 'mongoose';
And connect MongoDB in app.listen():
app.listen(8080, () => {
   console.log(`Started successfully server at port ${port}`);
   mongoose.connect('mongodb://localhost/test').then(() => {
      console.log(`Conneted to mongoDB at port 27017`);
   });
});
Schema
$ mkdir database && mkdir database/models && mkdir database/schemas
$ touch database/schemas/user.schema.js
$ npm i sha256
First, create the schema, and initialize all the attributes for that object in the database.
For example, the User schema will have two attributes: email & hashedPassword.
Open user.schema.js:
import { Schema } from 'mongoose';
import sha256 from 'sha256';

const userSchema = new Schema({
 hashedPassword: { type: String, required: true },
 email: { type: String, required: true },
});

/**
 * @param {*} password
 */
userSchema.methods.comparePassword = function comparePassword(password) {
 return this.hashedPassword === sha256(password);
};

export default userSchema
Models
Then, you want to create a model for that each schema you create and add them into index.js (so you only need to call one file):
$ touch database/models/user.model.js
Open user.model.js:
import mongoose from 'mongoose';
import userSchema from '../schemas/user.schema';

const User = mongoose.model('User', userSchema);

export default User;
Open models/index.js:
import User from './user.model';

export {
   User,
};

Save data using API

Open controller/user.controller.js.
Import User & replace userController.get(‘/’, …) with these 2 new APIs(Endpoints):
import { 
 User
} from '../database/models';
import sha256 from 'sha256';
/**
 * GET/
 * retrieve and display all Users in the User Model
 */
userController.get('/', (req, res) => {
   User.find({}, (err, result) => {
      res.status(200).json({
         data: result,
      })
   })
});
/**
 * POST/
 * Add a new User to your database
 */
userController.post('/add-user', (req, res) => {
   const { email, password } = req.body;
 
   const userData = {
      email,
      hashedPassword: sha256(password)
   }
const newUser = new User(data);
newUser
      .save()
      .then(data => {
         res.status(200).send(data);
      })
      .catch(err => {
         res.status(400).send("unable to save to database");
      });
});

Starting

Start Database:
$ mongod -config /usr/local/etc/mongod.conf
Start server
$ npm start

Postman

Open Postman, if you don’t know how to use it. Please watch this tutorial:
Use POST/ method and enter localhost:8080/add-user. This will call the “/add-user” API.
Add this to your body (Postman), If you don't know where to put this. Please watch the video first.
{
 'email': 'calvin.nvqc@gmail.com',
 'password': '123456789'
}
Check if your user data is saved to the database, open Web Browser and enter localhost:8080/
Now, you’re done!
Congratulations on building your first API.

Project Structure

project
 ├── client
 └── server
   └── controller - Storing APIs of the app (GET, POST, PUT, DELETE)
    ├── index.js
    └── user.controller.js
   └── database 
    ├── model - store all the models of the project
    └──schema - create attribute for each model
   ├── global.js - storing your configuration attribute
   ├── .eslintrc - config ESLint Airbnb Coding Style
   ├── .babelrc - migrate ES6 -> ES5 to run on different browsers
   ├── package.json - config ESLint Airbnb Coding Style
   └── App.js - Everything a server needs to start

Written by calvinqc | A Full-stack Web / iOS Developer, and an inspired mentor.
Published by HackerNoon on 2019/10/26