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.
JWT Token adalah JSON Web Token, yang berarti token ini menggunakan JSON (Javascript Object Notation) berbentuk string panjang yang sangat random, lalu token ini memungkinkan kita untuk mengirimkan data yang dapat diverifikasi oleh dua pihak atau lebih. JWT ini tidak tergantung sama bahasa program tertentu jadi kita bisa mengimplementasikan di Laravel Codeigniter Node JS dan yang lainnya
Dini saya akan menggunakan Express.js sebagai frameworknya dimana pada tahap sebelumnya sudah dijelaskan mengenai express.js. Fitur project yang akan dibuat sebagai berikut.
Dini saya akan menggunakan Express.js sebagai frameworknya dengan skeleton bawaan Express.js
npm install express-generator -g # -g option untuk installasi secara global
express app-exseq #create project
cd app-exseq
npm install
npm install sequelize mysql2 --save
sequelize init
Perintah di atas menghasilkan 4 folder dan beberapa file, yaitu
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": {
...
}
...
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
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
Selanjutnya kita akan membuat assosiation berdasarkan skema :
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)
}
}
...
};
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'
});
}
}
...
};
sequelize db:migrate
Perintah tersebut akan menggenerate table2 berikut:
Skema Controller yang akan dibuat :
Sebelum memulai kita membutuhkan library tambahan untuk melakukan fitur login yaitu
npm install dotenv jsonwebtoken bcryptjs --save
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
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 })
})
}
}
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();
});
},
}
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);
npm start
Dengan adanya JWT Token, kita akan melindungi module Post dengan Middleware JWT Token.
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 })
})
},
}
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();
});
}
}
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);