Table of Contents

03 – Express.js + ORM Squelize + JWT Token

Pengenalan

Sequelize adalah Node.js promise-based ORM untuk MySQL, PostgreSQL, SQLite, MSSQL dan database SQL lainnya. Sequelize berfungsi  untuk bekerja dengan database dan relasi-relasi di dalamnya. Sehingga pada saat deployment  kamu  tidak perlu melakukan perubahan konteks saat menuliskan kode karena  kamu sudah membuat interaksi menggunakan bahasa Javascript melalui api yang sudah disediakan oleh Sequelize.

Salah satu fitur terbaik dari Sequelize adalah kamu bisa menggunakan api yang sama untuk database yang berbeda.

Dini saya akan menggunakan Express.js sebagai frameworknya dimana pada tahap sebelumnya sudah dijelaskan mengenai express.js. Fitur project yang akan dibuat sebagai berikut.

  1. Login & Register
  2. Post + Category Many To Many, CRUD
Installasi

Dini saya akan menggunakan Express.js sebagai frameworknya dengan skeleton bawaan Express.js

Install Express.js
				
					npm install express-generator -g # -g option untuk installasi secara global
express app-exseq #create project
cd app-exseq
npm install
				
			
Install Sequelize
				
					npm install sequelize mysql2 --save
sequelize init
				
			
  1. Perintah –save di atas bertujuan untuk menyimpan modul yang kita pakai tadi ke file package.json
  2. Setelah itu, kita akan membuat direktori yang diperlukan untuk koneksi ke DB dengan perintah init

Perintah di atas menghasilkan 4 folder dan beberapa file, yaitu

  1. config/config.json //kofigurasi database
  2. models/index.js //index importer model
  3. migrations,
  4. seeders
Konfigurasi Database

Untuk mengkonfigurasi Database, buka file config/config.json. Disitu tertera 3 jenis config, karena kita sedang di tahap development maka kita akan mengkonfigurasikan koneksi Database pada config development.

				
					{
  "development": {
    "username": "saya",
    "password": "passwordsaya",
    "database": "blog_db",
    "host": "127.0.0.1",
    "dialect": "mysql"
  },
  "test": {
    ...
  },
  "production": {
    ...
  }
...
				
			
Membuat Models, dan Migrations
User Migration

Sebelum memulai membuat API Endpoint, kita harus membuat model sebagai penghubung aplikasi ke DB, buka terminal dan tuliskan perintah berikut.

				
					sequelize model:create --name users --attributes name:string,email:string,password:string
				
			

Perintah di atas akan men-generate file

  1. models/users.js
  2. migrations/xxx-create-users.js
Category & Post Migration

Lanjutkan dengan menggenerate table lainnya.

				
					sequelize model:create --name categories --attributes name:string,slug:string
sequelize model:create --name posts --attributes title:string,slug:string,content:string
				
			
Assosiation

Selanjutnya kita akan membuat assosiation berdasarkan skema :

  1. users has-many posts
  2. categories has-many posts
  3. posts belong-to users, has-many categories
  4. post_categories link many posts to many categories
User one-to-many Post

Buka migrations/xxx-create-posts.js, tambahkan field berikut.

				
					'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
  async up(queryInterface, Sequelize) {
    await queryInterface.createTable('posts', {
      ...
      userId: {
        type: Sequelize.INTEGER,
        references: { model: 'users', key: 'id' },
        onDelete: 'CASCADE'
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATE
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE
      }
    });
  },
  async down(queryInterface, Sequelize) {
    await queryInterface.dropTable('posts');
  }
};
				
			

Buka file models/users.js, tambahkan baris berikut

				
					...
module.exports = (sequelize, DataTypes) => {
  class users extends Model {
    ...
    static associate(models) {
      this.hasMany(models.posts)
    }
  }
  ...
};
				
			

Buka file models/posts.js, tambahkan baris berikut

				
					...
module.exports = (sequelize, DataTypes) => {
  class posts extends Model {
    ...
    static associate(models) {
      this.belongsTo(models.users)
    }
  }
  ...
};
				
			
Post many to many Categories

Untuk case ini, kita memerlukan table baru yaitu post_categories. Kita buat migrationnya terlebih dahulu.

				
					sequelize model:create --name post_categories --attributes postId:integer,categoryId:integer
				
			

Buka file migrations/xxx-create-post-categories.js, tambahkan assosiation reference foreign-key ke table posts dan categories

				
					...
module.exports = {
  async up(queryInterface, Sequelize) {
    await queryInterface.createTable('post_categories', {
      ...
      postId: {
        type: Sequelize.INTEGER,
        references: { model: 'posts', key: 'id' },
        onDelete: 'CASCADE'
      },
      categoryId: {
        type: Sequelize.INTEGER,
        references: { model: 'categories', key: 'id' },
        onDelete: 'CASCADE'
      },
      ...
    });
  },
  ...
};
				
			

Buka file models/posts.js, tambahkan baris berikut.

				
					...
module.exports = (sequelize, DataTypes) => {
  class posts extends Model {
    ...
    static associate(models) {
      ...
      this.belongsToMany(models.category, {
        through: 'post_categories',
        as: 'categories',
        foreignKey: 'post_id'
      });
    }
  }
  ...
};
				
			

Buka file models/categories.js, tambahkan baris berikut.

				
					...
module.exports = (sequelize, DataTypes) => {
  class categories extends Model {
    ...
    static associate(models) {
      this.belongsToMany(models.posts, {
        through: 'post_categories',
        as: 'post',
        foreignKey: 'category_id'
      });
    }
  }
  ...
};
				
			
Jalankan Migration
				
					sequelize db:migrate
				
			

Perintah tersebut akan menggenerate table2 berikut:

  1. categories
  2. post_categories
  3. posts
  4. users
Auth Register & Login

Skema Controller yang akan dibuat :

  1. Auth/Register
  2. Auth/Login

Sebelum memulai kita membutuhkan library tambahan untuk melakukan fitur login yaitu

  1. dotenv
  2. bcypt 
  3. jsonwebtoken
				
					npm install dotenv jsonwebtoken bcryptjs --save
				
			
Setting JWT_KEY

Buat file baru bernama .env letakan di main direktori project, tambahkan variable berikut.

				
					JWT_SECRET="passwordnyapanjangbanget"
				
			

Buka file app.js lalu tambahkan baris berikut sebelum baris “var app=express()”

				
					...
require('dotenv').config()
var app = express();
...
				
			

Selanjutnya JWT_SECRET dapat dipanggil menggunakan variable process.env.JWT_SECRET

Controller

Buat folder controllers, lalu buat file baru controllers/auth.js

				
					const db = require("../models");
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');

module.exports = {
    doRegister(req, res){
        db.users.create({
            name: req.body.name,
            email: req.body.email,
            password: bcrypt.hashSync(req.body.password, 8)
        }).then(user=>{
            return res.status(201).json({
                user,
                message:"User was registered succesfully"
            });
        }).catch(error =>{
            return res.status(500).json({ error: error.message })
        })
    },
    doLogin (req, res) {
        db.users.findOne({ 
            where:{ 
                email: req.body.email ,
            }
        }).then(user=>{
            if (!user) {
                return res.status(404).send({ message: "User Not found." });
            }
            var passwordIsValid = bcrypt.compareSync(
                req.body.password,
                user.password
            );
        
            if (!passwordIsValid) {
                return res.status(401).send({
                  accessToken: null,
                  message: "Invalid Password!"
                });
            }
        
            var token = jwt.sign({ id: user.id }, process.env.JWT_SECRET, {
                expiresIn: 86400 // 24 hours
            });
            res.status(201).json({
                id: user.id,
                username: user.username,
                email: user.email,
                accessToken: token
            });
        }).catch(error=>{
            res.status(500).json({ error: error.message })
        })
    }
}
				
			
Middeware

Siapkan Middleware untuk menangani verifikasi Register & Login. Buat folder middlewares, lalu buat file baru middlewares/verifyAuth.js

				
					const db = require("../models");

module.exports = {
    registerDuplicateEmail(req, res, next) {
        db.users.findOne({
            where: {
                email: req.body.email
            }
        }).then(user => {
            if (user) {
                res.status(400).send({
                    auth: false,
                    message: "Error",
                    errors: "Email is already taken!"
                });
                return;
            }
            next();
        });
	},
	loginCheckEmail(req, res, next) {
        db.users.findOne({
            where: {
                email: req.body.email
            }
        }).then(user => {
            if (!user) {
                res.status(400).send({
                    auth: false,
                    message: "Error",
                    errors: "Email not Exist"
                });
                return;
            }
            next();
        });
	},
}
				
			
Route

Buat file routes/auth.js

				
					var controller = require("../controllers/auth");
const verifyAuth = require('../middlewares/verifyAuth');
var express = require('express');
var router = express.Router();

router.post("/register",[verifyAuth.registerDuplicateEmail],controller.doRegister);
router.post("/login",[verifyAuth.loginCheckEmail],controller.doLogin);

module.exports = router;

				
			

Lalu  buka file app.js tambahkan baris2 berikut

				
					...
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var authRouter = require('./routes/auth');
...
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/auth', authRouter);
				
			
Jalankan Server & Test Postman
				
					npm start
				
			
Register
Login
Posts

Dengan adanya JWT Token, kita akan melindungi module Post dengan Middleware JWT Token.

Controller

Buat file controllers/post.js

				
					const db = require("./../models");

module.exports = {
    getData(req, res) {
        db.posts.findAll({
            include: [{
                model: db.categories,
                as: 'categories',
                required: false,
                through: { attributes: [] }
            }],
        }).then(posts=>{
            return res.status(201).json({
                id:req.userId,
                posts,
            });
        }).catch(error=>{
            return res.status(500).json({ error: error.message })
        })
    },
    createData (req, res) {
        db.posts.create(req.body).then(
            post=>{
                const categories= req.body.categories
                const result=req.body
                result.id=post.id
                categories.forEach(c => {
                    db.post_categories.create({
                        "postId":post.id,
                        "categoryId": c.id
                    })
                });
                return res.status(201).json({
                    result
                });
            }
        ).catch(error=>{
            return res.status(500).json({ error: error.message })
        })        
    },
    updateData (req, res) {
        db.posts.update(req.body,{
            where: {
                id: req.params.id
            }
        }).then(
            id=>{
                db.post_categories.destroy({
                    where:{
                        postId:id
                    }
                }).then(()=>{
                    const categories= req.body.categories
                    const result=req.body
                    result.id=id
                    categories.forEach(c => {
                        db.post_categories.create({
                            "postId":id,
                            "categoryId": c.id
                        })
                    });
                    return res.status(201).json({
                        result
                    });
                })
                
            }
        ).catch(error=>{
            return res.status(500).json({ error: error.message })
        })        
    },
    deleteData (req, res) {
        db.posts.destroy({
            where:{
                id:req.params.id
            }
        }).then(
            id=>{
                return res.status(201).json({
                    id
                });
            }
        ).catch(error=>{
            return res.status(500).json({ error: error.message })
        })        
    },
    
}


				
			
Middleware

Buat file middlewares/verifyJwtToken.js

				
					const jwt = require('jsonwebtoken');

module.exports = {
	verifyToken(req, res, next) {
		let tokenHeader = req.headers['x-access-token'];
        if(!tokenHeader){
            return res.status(500).send({
				auth: false,
				message: "Error",
				errors: "Token not exists"
			});
        }
		if (tokenHeader.split(' ')[0] !== 'Bearer') {
			return res.status(500).send({
				auth: false,
				message: "Error",
				errors: "Incorrect token format"
			});
		}

		let token = tokenHeader.split(' ')[1];

		if (!token) {
			return res.status(403).send({
				auth: false,
				message: "Error",
				errors: "No token provided"
			});
		}

		jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
			if (err) {
				return res.status(500).send({
					auth: false,
					message: "Error",
					errors: err
				});
			}
			req.userId = decoded.id;
			next();
		});
	}
}
				
			
Route

Buat file routes/posts.js

				
					const controller = require("../controllers/post");
const verifyJwtToken = require('../middlewares/verifyJwtToken');

var express = require('express');
var router = express.Router();

router.get("/",[verifyJwtToken.verifyToken],controller.getData);
router.post("/",[verifyJwtToken.verifyToken],controller.createData);
router.put("/:id",[verifyJwtToken.verifyToken],controller.updateData);
router.delete("/:id",[verifyJwtToken.verifyToken],controller.deleteData);

module.exports = router;

				
			

Buka kembali app.js lalu tambahkan pengaturan route posts berikut.

				
					...
var authRouter = require('./routes/auth');
var postsRouter = require('./routes/posts');
...
app.use('/auth', authRouter);
app.use('/posts', postsRouter);
				
			
Restart & Test Postman
Github Link