Part 1. Project Setup:

We will have 3 services in our pm2 to keep things simple.

pm2-auth

pm2-api

pm2-mongodb

The architecture of the application can be very complex as well, but it is recommended to move to more reliable infrastructure options, such as containers (docker). But up to mid-level applications, this architecture could be utilized very smoothly.

We will build 2 services namely pm2-api and pm2-auth, the prefix “pm2-” is to identify that our services are related. And we will also have a mongodb provisioning process inside pm2 as well named pm2-mongodb.

Quick start:

If you are going for quick start please skip the following steps and move forward to step#2

If you have a good grasp on creating the services on node js you can skip their creation and jump right to the deployment process, you will have to fork this repository https://github.com/imixtron/pm2-microservices.git Then go ahead and Clone that repository for a quick start:

git clone https://github.com/<your_username>/pm2-microservices.git

Detailed:

If you are new to JavaScript and Node Scene, i would recommend this Beginner level tutorial that will get you started.

Lets start by creating a folder where our codebase is going to be

$ mkdir pm2-microservices

$ cd pm2-microservices

Now we will be creating separate folders for each service having prefix ‘pm2-’, with the following folder structure, so go ahead and create the files and folders.

•

├── pm2-api

│ ├── .env

│ ├── index.js

│ └── package.json

│

├── pm2-auth

│ ├── .env

│ ├── index.js

│ └── package.json

│

└── pm2-mongodb

├── .gitkeep

We will leave pm2-mongodb folder empty as it will contain our data files, we can also later on mount this to a persistent storage, so it is independent of our application server or completely change the location as we require. Since the projects are similar, we will go ahead and populate the package.json with following content:

// pm2-api/package.json

{

"name": "pm2-api",

"version": "1.0.0",

"main": "index.js",

"scripts": {

"build": "npm install && npm start",

"start": "node index.js",

},

"dependencies": {

"body-parser": "^1.19.0",

"dotenv": "^8.0.0",

"express": "^4.17.0",

"mongoose": "^5.5.11"

}

} // pm2-auth/package.json

{

"name": "pm2-auth",

"version": "1.0.0",

"main": "index.js",

"scripts": {

"build": "npm install && npm start",

"start": "node index.js",

},

"dependencies": {

"body-parser": "^1.19.0",

"dotenv": "^8.0.0",

"express": "^4.17.0",

"mongoose": "^5.5.11"

}

}

Now that we have our dependencies in order, we can go ahead and hit npm install in our main folders pm2-api and pm2-auth. Our .env file will be similar having common contents with a tiny change of PORT, for pm2-api we will be using 9999 whereas for pm2-auth we will be using port 4000.

.env

PORT = 9999 # global environment

MONGO_URI = 'mongodb://localhost:27017'

MONGO_DB_NAME = 'serviceDB'

pm2-api (index.js)

const dotenv = require('dotenv').config();

const express = require('express');

const app = express();

const bodyParser = require('body-parser');

const mongoose = require('mongoose');

const Schema = mongoose.Schema;

const mongoUri = process.env.MONGO_URI;

const mongoDbName = process.env.MONGO_DB_NAME; // connect to mongoose instance (default config)

mongoose.connect(`${mongoUri}/${mongoDbName}`, { useNewUrlParser: true }); // user model

const userSchema = new Schema({

name: String,

email: String,

password: String

}); const User = mongoose.model('users', userSchema); // configuring bodyParser for POST data app.use(bodyParser.urlencoded({ extended: true }));

app.use(bodyParser.json()); const port = process.env.PORT || 9999; // API Routes

const router = express.Router(); router.post('/user', (req, res) => {

const userData = req.body;

const user = new User(userData); user.save((error) => {

if (!!error)

return res.status(500).send({error: error}); return res.status(500).send({

message: 'user created successfully'

});

});

}); app.use('/api', router);

app.listen(port);

console.log(`port ${port}`);

pm2-auth (index.js)

const dotenv = require('dotenv').config();

const express = require('express');

const app = express();

const bodyParser = require('body-parser');

const mongoose = require('mongoose');

const Schema = mongoose.Schema;

const mongoUri = process.env.MONGO_URI;

const mongoDbName = process.env.MONGO_DB_NAME; // connect to mongoose instance (default config)

mongoose.connect(`${mongoUri}/${mongoDbName}`, { useNewUrlParser: true }); // user model

const userSchema = new Schema({

name: String,

email: String,

password: String

}); const User = mongoose.model('users', userSchema); // configuring bodyParser for POST data

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

app.use(bodyParser.json()); const port = process.env.PORT || 4000; // API Routes

const router = express.Router(); router.post('/login', (req, res) => {

const {email, password} = req.body;

User.findOne({ email: email}, (error, user) => {

if (error)

return res.status(500).send({

error: true,

message: 'Invalid credentials'

}); if (user.password === password)

return res.status(200).send({

token: Buffer.from(user.password).toString('base64'),

...user.toObject()

}); return res.status(200).send({

message: 'Invalid Credentials'

});

});

}); app.use('/auth', router);

app.listen(port);

console.log(`port ${port}`);

As you can clearly see, to keep the tutorial simple, both of our services are exactly similar except for the endpoint functionality. We can have multiple services that work independently from each other.

Do note that we are using dotenv to parse .env file for local development. When we deploy this to server naturally we will have our own environment variables set via pm2 for each process.

pm2-mongodb