Dhruvang Gajjar
Posted on January 19, 2021
Built Authentication REST APIs using expressJS, MySQL, Sequelize and JWT. We will use following dependencies to built authentication APIs
Required Tool
Let’s take a moment to review the tools we’re going to be using:
- NodeJS : We’re going to use this to run JavaScript code on the server. I’ve decided to use the latest version of Node, v6.3.0 at the time of writing, so that we’ll have access to most of the new features introduced in ES6.
- Express : As per their website, Express is a “Fast, unopinionated, minimalist web framework for Node.js”, that we’re going to be building our Todo list application on.
- NPM : for the package management (both server, frontend, and development packages). It’ll be easier to maintain one package management system, than using NPM and Bower together.
- MySQL : This is a powerful open-source database that we’re going to use to store our Todos.
- Sequelize : In addition, we’re going to use Sequelize, which is a database ORM that will interface with the Mysql database for us.
- Postman : A Chrome app that we’ll use to practically test our API.
Create Project
Let’s begin by setting up our workspace.
You all are familiar with NPM. Before setup the project, open the terminal and check node and npm version. If version is displaying its means node and npm installed. If not then you must have to install the node and npm.
- Open CLI and go to the project directory
- Now type
npm init
to initialize the node project.
This command prompts you for a number of things, such as the name and version of your application. For now, you can simply hit RETURN to accept the defaults for most of them, with the following exception:
entry point: (index.js)
Enter app.js
, or whatever you want the name of the main file to be. If you want it to be index.js, hit RETURN to accept the suggested default file name.
This command will generate package.json
file in the project folder.
Setup Express
Initially I will make routes for the project. Install Express and a few of it’s dependencies.
- Open CLI and go to the project directory
- Type
npm i --save express cors body-parser dotenv
The --save
flag will save these packages to the dependencies section of your package.json file.
- Create a file in the root folder and call it
app.js
. - In this file, let’s create our Express application.
const express = require("express"),
bodyParser = require('body-parser'),
cors = require('cors'),
PORT = 8080;
require('dotenv').config()
const app = express()
app.use(cors())
app.use(bodyParser.json())
app.get("/", (req, res) => {
res.json({ "message": "Hello ChampDecay" })
})
app.listen(PORT, async () => {
console.log(`App is running on http://localhost:${PORT}`);
})
The application will be run scuccessfully on http://localhost:8080
We’ll need a way to restart the server every time we change something in our code. For that, we’ll use nodemon npm package.
npm i nodemon
Then, open up your package.json
file and create a command to run the server. That command will be created under the scripts section. Edit your package.jsonin the scripts section as follows:
...
"scripts": {
"dev": "nodemon app.js"
},
...
Now try running the application by executing following command in cli.
npm run dev
and visiting http://localhost:8080
. You should see
{
"message": "Hello ChampDecay"
}
At this point in time, your project structure should look like:
root
├── app.js
├── package.json
└── node_modules
Sequelize Setup
For this part, we are going to install MySQL.
Next, we will require Sequelize. This is an ORM that will interface with the MYSQL database for us.
We will use of the Sequelize CLI package to bootstrap the project for us. It will also help us generate database migrations.
So Let’s install Sequelize CLI package.following command will install sequelize-cli globally
npm install -g sequelize-cli
- Now we will install Sequelize package, alongside its dependencies. Sequelize is an easy-to-use multi SQL dialect ORM for Node.js. We gonna use MySQL as our database. So let install Sequelize ORM and mysql2 dialect.
>
npm i sequelize mysql2
Initialize sequelize
Let's generate generate migrations, seeders, config and models directories and config file using sequelize cli.
sequelize init
Above command will generate boilerplate code in your project, Now the project structure should look like:
root
├── app.js
├── package.json
├── config
│ └── config.json
├── migrations
├── models
│ └── index.js
└── seeders
In models/index.js
file, It establishes database connection by using config/config.json
. So let's configure config.json
.
{
"development": {
"username": "root", // Database Username
"password": null, // Database Password
"database": "blog", // Database Name
"host": "127.0.0.1",// Database Host
"dialect": "mysql"
},
"test": {
"username": "root",
"password": null,
"database": "database_test",
"host": "127.0.0.1",
"dialect": "mysql"
},
"production": {
"username": "root",
"password": null,
"database": "database_production",
"host": "127.0.0.1",
"dialect": "mysql"
}
}
Now as we want to create a authentication, we have to create a table for Users
. So we would generate migration and model for Users. Let's create model and migration by sequelize cli command as follows:
sequelize model:create --name User --attributes username:string,email:string,password:string
This will generate user.js
file in model directory and <timestamp>-create-user.js
migration in migration directory.
Generated user.js
model file will be look like:
'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class User extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
};
User.init({
username: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING
}, {
sequelize,
modelName: 'User',
});
return User;
};
and generated <timestamp>-create-user.js
migration file will be look like:
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
username: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING
},
password: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Users');
}
};
Now we have to add some settings in model and migration files like unique key and allow/disallow null values. So let's modify user.js
model file:
...
User.init({
username: {
type: DataTypes.STRING,
unique: true,
allowNull: false
},
email: {
type: DataTypes.STRING,
unique: true,
allowNull: false
},
password: {
type: DataTypes.STRING,
allowNull: false
}
}
...
Here, We have added unique
and allowNull
in fields and same as model, add these parameters in migration file as well.
Finally, models/user.js
will look like:
'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class User extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
};
User.init({
username: {
type: DataTypes.STRING,
unique: true,
allowNull: false
},
email: {
type: DataTypes.STRING,
unique: true,
allowNull: false
},
password: {
type: DataTypes.STRING,
allowNull: false
}
}, {
sequelize,
modelName: 'User',
});
return User;
};
and migrations/<timestamp>-create-user.js
file will look like:
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
username: {
type: Sequelize.STRING,
unique: true,
allowNull: false
},
email: {
type: Sequelize.STRING,
unique: true,
allowNull: false
},
password: {
type: Sequelize.STRING,
allowNull: false
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Users');
}
};
After setting up model and migrations, we will setup routes and controllers for user.
Setup Route
Create a file routes/user.js
at root directory of the project.
It will be look like:
const express = require('express'),
router = express.Router();
router.post('/signup', (req, res) => {
res.json({ "msg": "Signup Route" })
});
router.post('/signin', (req, res) => {
res.json({ "msg": "Signin Route" })
});
module.exports = router
Here we have create two routes, one for signup and another for signin. now this file should be called in app.js
Add following code in app.js
before app.listen command.
app.use('/api', require('./routes/user'))
That's it! We have setup routes for signup and signin. Our next step will be settingup the controller for user.
Setup Controller
Here, we will use jsonwebtokens for api authentications, So let's install bcryptjs
and jsonwebtoken
packages.
npm i bcryptjs jsonwebtoken
After adding two packages, create a controllers/user.js
file and add following code:
const bcrypt = require("bcryptjs"),
jwt = require('jsonwebtoken'),
db = require("../models/index"),
JWT_SECRET = process.env.JWT_SECRET
exports.signUp = async (req, res) => {
const { username, email, password: plainTextPassword } = req.body;
const password = await bcrypt.hash(plainTextPassword, 10)
try {
const user = await db.User.create({
username,
email,
password
})
res.status(201).json({ "status": "ok", "message": "User registered", user })
} catch (error) {
res.status(401).json({ "status": "error", "message": error.errors[0].message })
}
}
exports.signIn = async (req, res) => {
const { email, password } = req.body;
const user = await db.User.findOne({ where: { email: email } })
if (!user) {
return res.status(401).json({ status: 'error', error: 'Invalid username/password' })
}
if (await bcrypt.compare(password, user.password)) {
const payload = { id: user.id, username: user.username };
const options = { expiresIn: '2d', issuer: 'http://localhost:8080' };
const secret = JWT_SECRET;
const token = jwt.sign(payload, secret, options)
return res.status(200).json({ status: 'ok', "message": "User signin successful", token })
}
return res.status(401).json({ "status": "error", "message": "Invalid Username/Password" })
}
exports.getUsers = async (req, res) => {
try {
const users = await db.User.findAll()
return res.status(200).json({ status: 'ok', users })
} catch (error) {
return res.status(401).json({ "status": "error", error })
}
}
In this file, we have imported bcryptjs
, jsonwebtoken
and index
model. We have define JWT_SECRET
variable which should be hidden and ideally fetch from .env
file. Thereafter we have exported two functions.
Signup function
Here we get all request parameters and define it by const { username, email, password: plainTextPassword } = req.body;
Then we have to hashed the password so no one can see it from database. To hash the password, we have used bcrypt's hash function. we have used 2 paramaters in hash function, first is plaintext password which should be encoded and second is salt.
After it, we have to store the values including new hashed password to the database. so using sequelize's create function we have stored it in databse.
Signin function
Here we get all request parameters same as signup function. Thereafter we fetch row from users table of the database using findOne function.
If it doesn't return any row that means a user enters wrong email so we have responsed invalid message wirh 401 status.
If It returns a row, we have to compare the password from database and user input. So again bcrypt will be used. the compare function of bcrypt will do the comparisation. If comparisation is true, we will generate access token using jwt otherwise returns error message with 401 status.
We have used username and id to create jwt token. Also we have set expire time and issuer of the token.
Get Users Function
This function simply fetch users from database. But to access this route, user should pass valid access token with the request and to validate the access token we have to make a middle ware.
Create a new file middleware/auth.js
and that file should have following code:
const jwt = require('jsonwebtoken');
module.exports = {
validateToken: async (req, res, next) => {
const authHeader = req.headers.authorization;
if (authHeader) {
const token = authHeader.split(' ')[1];
try {
const result = await jwt.verify(token, process.env.JWT_SECRET)
req.decoded = result;
next()
} catch (error) {
return res.status(401).json({ "status": "error", "message": "Invalid Authentication.", error })
}
} else {
return res.status(401).json({ "status": "error", "message": "Authentication error. Token required." })
}
}
}
Here we have imported only jsonwebtoken and create a function called validateToken
. It will take access token from the authorization headers and verify it using jwt.verify()
function. If it is verfied successfully, it will go for the next process by next()
function, otherwise it returns a error message of invalide access token with status code 401.
So finally we have created controller and route. Let's connect route with the controller. Open routes/user.js
file and replace the following code:
const express = require('express'),
router = express.Router(),
User = require("../controllers/user")
router.post('/signup', User.signUp);
router.post('/signin', User.signIn);
router.get('/users', Auth.validateToken, User.getUsers);
module.exports = router
Here in /users
route, we have used middleware as second argument and That's all.
Finally, The code is ready..!!
This is my first post. Let me know your views by commenting on it.
Posted on January 19, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.