Easy MVC backend with JWT authentication using express.js & MySql

Sajad Asadi
Aratta Studios
Published in
5 min readJul 26, 2019

Hello
In this story, I’m going to teach how to make an MVC backend with a maintainable code and a proper strategy pattern.
We are going to use this boilerplate that I have created to ease our way.

Boilerplate link:
https://github.com/aratta-studios/aratta-express-boilerplate

Project Setup & Configuration

First, install this provided installer:

npm i -g @aratta-studios/aratta-express

For creating the project and initial setup, open your terminal in your working folder and use this command:

aratta-express aratta-express-example

Then in phpMyAdmin, create a database named:

aratta_express_db

Now let’s insert database credentials in our project to connect the project with database

First edit env/index.js as

export default {
username: "yourUserName",
password: "yourPassword",
database: "aratta_express_db",
host: "127.0.0.1",
dialect: "mysql",
jwtSecret: 'randomstring'
}

Then edit config/config.json as

{
"development": {
"username": "yourUserName",
"password": "yourPassword",
"database": "aratta_express_db",
"host": "127.0.0.1",
"dialect": "mysql"
},
"test": {
"username": "yourUserName",
"password": "yourPassword",
"database": "aratta_express_db",
"host": "127.0.0.1",
"dialect": "mysql"
},
"production": {
"username": "yourUserName",
"password": "yourPassword",
"database": "aratta_express_db",
"host": "127.0.0.1",
"dialect": "mysql"
}
}

This file is a config for Sequelize ORMwhich I’m using in this boilerplate

Ps: if you using XAMPP for DB and has not changed the configurations, Username is root and no password needed

Migrations and creating required tables

For this feature, we are using Sequelize and this is a very useful feature to use

You can read more in this link

http://docs.sequelizejs.com/manual/migrations.html

Before we start, it’s better to delete all files in migration folder I just put them for example also delete all files except index.js in the models folder

Then use these commands to generate a skeleton for migrations and models

npx sequelize-cli model:generate --name User --attributes username:string,password:stringnpx sequelize-cli model:generate --name Contact --attributes phone_number:string,name:string,user_id:integer

This commands will generate some files in models and migrations folders

Now let’s edit them and prepare them for use in the next steps

Edit user_id object in migrations/some-numbers-create-contact.js file as following

user_id: {
type: Sequelize.INTEGER,
references: {
model: "Users",
key: "id"
}
}

Now edit models/index.js as

import Sequelize from 'sequelize';
import env from '../env';
import UserModel from '../models/user';
import ContactModel from '../models/contact';

const Op = Sequelize.Op;

const operatorsAliases = {....};

const sequelize = new Sequelize(env.database, env.username, env.password, {
operatorsAliases,
host: env.host,
dialect: env.dialect,
pool: {
max: 10,
min: 0,
acquire: 30000,
idle: 10000
}
})
;

/**
* Link our models with sequelize
*/
const User = UserModel(sequelize, Sequelize);
const Contact = ContactModel(sequelize, Sequelize);

User.hasMany(Contact,{foreignKey:'user_id'});

module.exports = {
User,
Contact
};

Now run this command to make tables in the defined database

npx sequelize-cli db:migrate

Well, now our database and models are up and running.

Authentication

Now, let’s create signup and a login API for our application

First, delete exampleController.js file in controllers folder I just made this file for example

Then create a file named phoneBookController.js in controllers folder and initiate it with

import {User, Contact} from '../models';
import bcrypt from "bcrypt";
import {jwtAuthenticate} from "../auth";
import QueryHelper from "../helpers/queryHelper";

export function login(req, res) {
jwtAuthenticate({
username: req.body.username,
password: req.body.password,
usersTable: 'users',
usernameField: "username",
passwordField: 'password',
},res);
}

export function signup(req, res) {
bcrypt.hash(req.body.password, 10, (err, hash) => {
console.log(req.body.password);
User.create({username: req.body.username, password: hash}).then(user => res.status(200).json({
type: 'success',
message: 'Signed up!',
data: user
}));
});
}

Now lets set some routes for our project in routes/index.js

import {login, signup} from "../controllers/phoneBookController";
import {isJwtAuthenticated} from "../auth";

module.exports = function(app){

app.post('/login', login);
app.post('/signup', signup);

//other routes..
};

Now if we run the project with this command:

npm start

We can send post requests to these url

http://localhost:4000/signup

With this body

{
"username":"aratta",
"password":"aratta123"
}

We will make a user

Then if we use this url

http://localhost:4000/login

With this body

{
"username":"aratta",
"password":"aratta123"
}

In our response we will receive a field in the header named Authorization with a content like this:

Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNTY0MTY1NzE2fQ.2RXk5GWZpETJnmUzDqhvAz8AjfSZjV_cfohKIPvGZfY

This is our JWT token and we must use it in APIs that needed authorization

CRUD

Now lets make a crud for contacts edit phoneBookController.js in controllers folder

import {User, Contact} from '../models';
import bcrypt from "bcrypt";
import {jwtAuthenticate} from "../auth";
import QueryHelper from "../helpers/queryHelper";
import fs from "fs";
import csv from "fast-csv";
export function login(req, res) {
jwtAuthenticate({
username: req.body.username,
password: req.body.password,
usersTable: 'users',
usernameField: "username",
passwordField: 'password',
},res);
}

export function signup(req, res) {
bcrypt.hash(req.body.password, 10, (err, hash) => {
console.log(req.body.password);
User.create({username: req.body.username, password: hash}).then(user => res.status(200).json({
type: 'success',
message: 'Signed up!',
data: user
}));
});
}

/**
*
*
@param req
*
@param res
*
@constructor
*/
export function createOrUpdateContact(req, res) {
const body = req.body;
new QueryHelper(Contact).createOrUpdate(body, res);
}

/**
* req.body: {where}
*
@param req
*
@param res
*/
export function readConditionalContacts(req, res) {
const body = req.body;
new QueryHelper(Contact).find(body, res,null).conditional();
}

/**
* req.body: Doctor
*
@param req
*
@param res
*/
export function readOneUser(req, res) {
const body = req.body;
const relations = [
{
model: Contact}
]
;
new QueryHelper(User).find(body, res,relations).one();
}


/**
* req.body: {where}
*
@param req
*
@param res
*/
export function deleteConditionalContacts(req, res) {
const condition = req.body.where;
new QueryHelper(Contact).delete(condition, res);
}

Then add these functions in routes with my jwtAuthentication middleware

import {
createOrUpdateContact, deleteConditionalContacts,
login,
readConditionalContacts,
readOneUser,
signup
} from "../controllers/phoneBookController";
import {isJwtAuthenticated} from "../auth";

module.exports = function(app){

app.post('/login', login);
app.post('/signup', signup);
app.post('/create-or-update-contact',isJwtAuthenticated, createOrUpdateContact);
app.post('/read-conditional-contacts',isJwtAuthenticated, readConditionalContacts);
app.post('/read-one-user',isJwtAuthenticated, readOneUser);
app.post('/delete-conditional-contacts',isJwtAuthenticated, deleteConditionalContacts);

//other routes..
};

Now we can use them with an Authorization field in our request’s head with that Bearer value that we received in the previous step.

E.g for create-or-update-contact we can use a body like this

{
"phone_number":"+1929394949",
"name":"aratta123",
"user_id":"1"
}

In the body, we must use keys with names exactly like our DB fields if needed.

For using conditional APIs like read-conditional-contacts we must use a key named where in our body something like:

{
"where":{"id":"1"}
}

Uploading a CSV and batch create data

In this step we want to do something cool, we want to upload a CSV with a request and insert the data in our database

edit phoneBookController.js in the controllers folder

First, make sure that you have imported these in your controller

import fs from "fs";
import csv from "fast-csv";

Then add this function

/**
*
@param req
*
@param res
*/
export function csvCreateContacts(req, res) {
// open uploaded file
const queryHelper = new QueryHelper(Contact);

csv.fromPath(req.file.path)
.on("data", function (data) {
queryHelper.createWithoutResponse({phone_number:data[0],name:data[1]}, res);
})
.on("end", function () {

fs.unlinkSync(req.file.path); // remove temp file
res.status(200).json({
type: 'success',
message: 'createdAll!',
data: null
});
})
}

Now edit routes/index.js file

First, make sure that you have imported this and csvCreateContacts function

import multer from 'multer';

Then initiated

const upload = multer({ dest: 'tmp/csv/' });

Now add a new route like

app.post('/csv-create-contacts',isJwtAuthenticated,upload.single('file'),csvCreateContacts);

Now you can upload a CSV file with the first column of phone numbers and second column of names and use it in the request body as a form

That’s it, guys we just made a back-end app with express js using my boilerplate

If you had any problem just take a look at this repository

https://github.com/aratta-studios/aratta-express-example

--

--