FullStack Development with M.E.R.N Stack: Part 1
By: Calvin Nguyen
Tags: JavaScript, Mongodb, Nodejs, ES6, Expressjs

Install

Before you start, you need to install a few dependencies on your computer:

Express Middleware

Install the first 3 middlewares to run your App:

$ npm i express esm nodemon --save

Express : an essential framework for NodeJS to start a server project.

: an essential framework for NodeJS to start a server project. esm : This goes with babel, which you will install later, and allows you to run ES6 (New version of Javascript).

This goes with and allows you to run ES6 (New version of Javascript). nodemon: This is my favorite; it will enable you to restart the server automatically whenever you make changes in the server.

Now, your package.json will look something like

{

"name": "server",

"version": "1.0.0",

"private": true,

"scripts": {

"start": "node -r esm app.js",

"dev": "nodemon -r esm app.js"

},

"dependencies": {

"express": "^4.17.1",

"esm": "^3.2.25",

"nodemon": "^2.0.2"

},

"devDependencies": {

"babel-cli": "^6.26.0",

"babel-core": "^6.26.3",

"babel-polyfill": "^6.26.0",

"babel-preset-es2015": "^6.24.1",

"babel-preset-stage-2": "^6.24.1",

"babel-register": "^6.26.0"

}

}

ESLint Airbnb + Prettier + Babel

Install Eslint & Prettier: npm i eslint prettier

Install ESLint Airbnb to allow all developers to have the same coding style and follow one Javascript coding style

$ npx install-peerdeps --dev eslint-config-airbnb

Create .eslintrc in your server folder and add this:

{

“extends”: “airbnb”

}

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 --save-dev @babel/core

Create .babelrc file in your server project and add this:

gitignore

Create a .gitignore file in your server project and add this:

package-lock.json

node_modules/

This is so familiar with every developer nowadays, but for those who are new: .gitignore will ignore those files/folders when pushing to Github/Gitlab/Bitbucket

In simple words, I don’t want to add node_modules/ to git because it’s HUGE, and others can just install with package.json.

So, I add node_modules/ to .gitignore, and it won’t be pushed to Github/Gitlab/Bitbucket.

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.

const app = express(); // Use your dependencies here // use all controllers(APIs) here

app.get(‘/’, (req, res) => {

res.status(200).json({

status: ‘Server Run successfully’

});

}); // Start Server here

app.listen(8080, () => {

console.log(‘Server is running on port 8080!’);

});

Start your Server

You can find the script that runs these functions in package.json

$ npm start

This will run “start”: “node -r esm app.js” in the package.json

node: this is the main command to run a node app

this is the main command to run a node app -r esm: Enable ‘esm’ for local runs

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 is body-parser:

$ npm i body-parser

Import those highlight lines to app.js:

// Import all dependencies & middleware here

import express from 'express';

import bodyParser from 'body-parser'; // Init an Express App.

const app = express(); // Use your dependencies here

app.use(bodyParser.urlencoded({ extended: false }));

app.use(bodyParser.json()); // use all controllers(APIs) here

app.get('/', (req, res) => {

res.status(200).json({

status: ‘Server Run successfully’

});

}); // Start Server here

app.listen(8080, () => {

console.log('Example app listening on port 8080!');

});

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: 'user Controller API call successfully'

});

}); 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 (APIs). 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’; export {

userController

};

Adding API to Express App

You just created the Controller (API), 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: ‘Server Run successfully’

});

});

with this:

app.use(‘/’, userController);

You will end up the app.js file like this:

// Import all dependencies & middleware here

import express from "express";

import bodyParser from "body-parser"; import { userController } from "./controller"; // Init an Express App.

const app = express(); // Use your dependencies here

app.use(bodyParser.urlencoded({ extended: false }));

app.use(bodyParser.json()); // use all controllers(APIs) here

app.use("/", userController); // Start Server here

app.listen(8080, () => {

console.log('Server is running on port 8080!');

});

NOTE: Test your app again to make sure it is working. This is so much cleaner and more organized when you develop more APIs in the future

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 & Run MongoDB

$ cd ~

$ brew update

$ brew tap mongodb/brew

$ brew install mongodb-community@4.2

$ brew services start mongodb-community

To stop MongoDB: simply run brew services stop mongodb-community

For more info, please refer to this doc: https://github.com/mongodb/homebrew-brew

Install Mongoose

Install Mongoose in your application: “Mongoose provides a straight-forward, schema-based solution to model your application data.”

This library makes your life easier when you work with MongoDB Driver.

$ npm i mongoose

Connection

In your app.js, import mongoose and connect your app with MongoDB:

// Import all dependencies & middleware here

import express from "express";

import bodyParser from "body-parser";

import mongoose from "mongoose"; import { userController } from "./controller"; // Init an Express App.

const app = express(); // Use your dependencies here

app.use(bodyParser.urlencoded({ extended: false }));

app.use(bodyParser.json()); // use all controllers(APIs) here

app.use("/", userController); // Start Server here

app.listen(8080, () => {

console.log("Server is running on port 8080!");

mongoose.connect("mongodb://localhost/test").then(() => {

console.log(`Conneted to mongoDB at port 27017`);

});

});

you will see 2 lines when you start your app again:

Server is running on port 8080!

Conneted to mongoDB at port 27017

Build a simple User’s Model & Schema Object for your Database

$ 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 },

});

*

*/

userSchema.methods.comparePassword = function comparePassword(password) {

return this.hashedPassword === sha256(password);

}; /** @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 && database/models/index.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):