Tutorial RESTful API Fastify JS + MySQL + Token Auth — Part 2
Simple Auth, dan CRUD dengan framework Node JS yang cepat bernama Fastify.
Halo gengs! Pada kali ini gue akan melanjutkan dari artikel sebelumnya yaitu Tutorial RESTful API Fastify JS + MySQL + Token Auth — Part 1.
Nah sekarang kita lanjut ya, karena kita udah berhasil ngebuat autentikasi maka kita akan lanjut. Selanjutnya kita akan membuat fungsi check token, yang berguna untuk mengecek apakah token tersebut masih aktif dan apakah token tersebut milik si pengecek.
async function checkToken (request, reply) {
let token = request.body.token.toString();
let user_token = request.body.user_token.toString();
let now = moment().format('YYYY-MM-DD HH:mm:ss').toString();
let sql = `SELECT authentication.*, users.remember_token FROM authentication
INNER JOIN users ON users.id = authentication.user_id WHERE authentication.id = ? AND users.remember_token = ?`;
let data = await new Promise((resolve) =>
connection.query(sql, [token, user_token], function (error, rows) {
if(error){
console.log(error);
return response.badRequest('', `${error}`, reply)
}
if(rows.length > 0){
return rows[0].remember_token === user_token ? resolve(rows[0].expires_at) : resolve(false);
}
else{
return response.badRequest({}, "Token yang kamu masukkan salah!", reply);
}
})
);
let array = { expires_at: data};
let message = moment(data).format('YYYY-MM-DD HH:mm:ss').toString() > now ?
'Token ini milikmu dan masih aktif hingga saat ini!' : "Token kamu sudah tidak aktif!";
return array ? response.ok(array, message, reply) : response.badRequest({}, message, reply);
}
Masukkan fungsi check token diatas, setelah itu jangan lupa masukkan si checkToken kedalam exports.
module.exports = {
createToken, checkToken
};
Tambahkan routes untuk check token
fastify.post('/api/token/check', auth.checkToken);
Sekarang jalankan dan masukkan token yang telah kamu buat pada tutorial sebelumnya dengan token dari user kamu yang dapatkan
Sekarang sudah bisa di cek dan kita akan membuat semacam helper untuk mendapatkan token yang akan diakses. Setiap token yang dikirim ke server, kita akan mendapatkan id user untuk API todo kita. Buat dulu helpernya dengan cara membuat folder helper di root folder lalu buat file token.js.
let response = require('../response');
let connection = require('../connection');
let moment = require('moment');
async function check(token, reply) {
let now = moment().format('YYYY-MM-DD HH:mm:ss').toString();
let sql = "SELECT * FROM authentication WHERE id = ?";
return new Promise((resolve) =>
connection.query(sql, [token], function (error, rows) {
if(error){
console.log(error);
return response.badRequest('', `${error}`, reply)
}
if(rows.length > 0){
let message = moment(rows[0].expires_at).format('YYYY-MM-DD HH:mm:ss').toString() > now;
if(message){
return resolve({ user_id: rows[0].user_id, secret_id: rows[0].secret_id,
token : rows[0].id });
}
else{
return response.badRequest('', "Masa Aktif Token telah habis!", reply);
}
}
else{
return response.badRequest('', 'Token yang anda masukkan salah!', reply)
}
})
);
}
module.exports = {
check
};
Masukkan kode diatas dan kita siap membuat controller todo.js, yuk dibuat.
let response = require('../response');
let connection = require('../connection');
let moment = require('moment');
let header = require('../helper/token');
async function get (request, reply) {
let token = request.headers.authorization;
let check = await header.check(token, reply);
let sql = `SELECT * FROM todo WHERE user_id = ?`;
let data = await new Promise((resolve) => connection.query(sql, [check.user_id], function (error, rows) {
if(error){
console.log(error);
return response.badRequest('', `${error}`, reply)
}
if(rows.length > 0){
let array = [];
rows.forEach(element => {
array.push({
id: element.id,
title: element.title,
description: element.description,
created_at: moment(element.created_at).format('YYYY-MM-DD HH:mm:ss').toString()
});
});
return resolve(array);
}
else{
return resolve([]);
}
})
);
return response.ok(data, '', reply);
}
async function show (request, reply) {
let id = request.params.id;
let token = request.headers.authorization;
let check = await header.check(token, reply);
let sql = `SELECT * FROM todo WHERE id = ? AND user_id = ?`;
let data = await new Promise((resolve) =>
connection.query(sql,
[id, check.user_id], function (error, rows) {
if(error){
console.log(error);
return response.badRequest('', `${error}`, reply)
}
return rows.length > 0 ? resolve({
id : rows[0].id,
title : rows[0].title,
description : rows[0].description,
created_at : moment(rows[0].created_at).format('YYYY-MM-DD HH:mm:ss').toString()
}) : resolve({});
})
);
return response.ok(data, '', reply);
}
async function store (request, reply) {
let now = moment().format('YYYY-MM-DD HH:mm:ss').toString();
let title = request.body.title;
let description = request.body.description;
let token = request.headers.authorization;
let created_at = now;
let updated_at = now;
let check = await header.check(token, reply);
let sql = `INSERT INTO todo (user_id, title, description, created_at, updated_at) values(?, ?, ?, ?, ?)`;
let data = await new Promise((resolve) =>
connection.query(sql,
[check.user_id, title, description, created_at, updated_at], function (error, rows) {
if(error){
console.log(error);
return response.badRequest('', `${error}`, reply)
}
return rows.affectedRows > 0 ? resolve(true) : resolve(false);
})
);
let msg = data ? "Berhasil menambahkan data!" : "Tidak berhasil menambahkan data!";
return response.ok({}, msg, reply);
}
async function update (request, reply) {
let now = moment().format('YYYY-MM-DD HH:mm:ss').toString();
let id = request.body.id;
let title = request.body.title;
let description = request.body.description;
let token = request.headers.authorization;
let created_at = now;
let updated_at = now;
let check = await header.check(token, reply);
let sql = `UPDATE todo set title = ?, description = ?, updated_at = ?) values(?, ?, ?) WHERE id = ? AND user_id = ?`;
let data = await new Promise((resolve) =>
connection.query(sql,
[title, description, created_at, updated_at, id, check.user_id], function (error, rows) {
if(error){
console.log(error);
return response.badRequest('', `${error}`, reply)
}
return rows.affectedRows > 0 ? resolve(true) : resolve(false);
})
);
let msg = data ? "Berhasil mengubah data!" : "Tidak berhasil mengubah data!";
return response.ok({}, msg, reply);
}
async function destroy (request, reply) {
let id = request.body.id;
let token = request.headers.authorization;
let check = await header.check(token, reply);
let sql = `DELETE FROM todo WHERE id = ? AND user_id = ?`;
let data = await new Promise((resolve) =>
connection.query(sql,
[id, check.user_id], function (error, rows) {
if(error){
console.log(error);
return response.badRequest('', `${error}`, reply)
}
return rows.affectedRows > 0 ? resolve(true) : resolve(false);
})
);
let msg = data ? "Berhasil menambahkan data!" : "Tidak berhasil menambahkan data!";
return response.ok({}, msg, reply);
}
module.exports = {
store, update, show, destroy, get
};
Nah sekarang kita perlu membuat middleware, gunanya adalah mencegah akses sembarangan yang masuk kedalam API kita sehingga API kita tidak akan diakses dengan mudahnya. Buat folder middleware dan file middleware-token.js.
let response = require('../response');
let connection = require('../connection');
let moment = require('moment');
async function check(request, reply) {
console.log("lewat middleware");
let token = request.headers.authorization;
let now = moment().format('YYYY-MM-DD HH:mm:ss').toString();
let sql = "SELECT * FROM authentication WHERE id = ?";
return new Promise((resolve) =>
connection.query(sql, [token.toString()], function (error, rows) {
if(error){
console.log(error);
return response.badRequest('', `${error}`, reply)
}
if(rows.length > 0){
let message = moment(rows[0].expires_at).format('YYYY-MM-DD HH:mm:ss').toString() > now;
if(message){
return resolve(true);
}
else{
return response.badRequest('', "Masa Aktif Token telah habis!", reply);
}
}
else{
return response.badRequest('', 'Token anda salah!', reply)
}
})
);
}
module.exports = {
check
};
Kalau sudah berarti kita siap membuat routesnya, silahkan tambahkan routes dibawah ini pada routes.js kita.
fastify.route({
method: 'GET',
url: '/api/todo',
preHandler: async function (request, reply, done) {
await middleware.check(request, reply);
done()
},
handler: todo.get
});
fastify.route({
method: 'GET',
url: '/api/todo/:id',
preHandler: async function (request, reply, done) {
await middleware.check(request, reply);
done()
},
handler: todo.show
});
fastify.route({
method: 'POST',
url: '/api/todo',
preHandler: async function (request, reply, done) {
await middleware.check(request, reply);
done()
},
handler: todo.store
});
fastify.route({
method: 'PUT',
url: '/api/todo',
preHandler: async function (request, reply, done) {
await middleware.check(request, reply);
done()
},
handler: todo.update
});
fastify.route({
method: 'DELETE',
url: '/api/todo',
preHandler: async function (request, reply, done) {
await middleware.check(request, reply);
done()
},
handler: todo.destroy
});
Fungsi preHandler adalah fungsi untuk mengeksekusi sebuah fungsi atau code yang terjadi sebelum handler dijalankan, sehingga sebelum kita mengeksekusi salah satu API Todo seperti GET, atau POST. Maka semua callback dari preHandler akan dijalankan terlebih dahulu dengan metode asynchronous.
Karena kita akan menjaga routes TODO kita dari akses API yang sembarangan maka kita hanya memasang middleware pada routes todo saja. Kita tidak akan memasukkan middleware untuk kelima routes dibawah yang telah kita tulis sebelumnya.
//Route Ujicoba
fastify.get('/', function (request, reply) {
reply.send({message: 'Hello World', code: 200});
});
fastify.post('/api/users/login', users.login);
fastify.post('/api/users/register', users.register);
fastify.post('/api/token', auth.createToken);
fastify.post('/api/token/check', auth.checkToken);
Apabila kamu ingin menjaga semua routes dengan menggunakan middleware ada trick yang dapat kamu lakukan. Caranya adalah:
Pisahkan routes yang ingin kamu jaga dengan middleware dengan routes normal yang tidak ingin dipasangkan middleware. Contoh kita membuat routes1.js dan routes2.js, lalu masukkan code dibawah ini pada routes yang ingin dipasangkan dengan middleware.
fastify.addHook('preHandler', async (request, reply, next) => {
//TODO: Write your code here
next();
});
Apabila kamu ingin memasang seluruh hook kamu dapat melakukannya dengan menambahkan code diatas pada CORE file kamu yaitu fastify.js.
Ada berbagai Hooks yang dapat dipasangkan, sisanya pada cek dokumentasi fastify.
Sekarang kamu dapat menjalankan API kamu, silahkan mengakses API todo kamu.
Nah sekarang kamu telah berhasil membuat API lengkap dengan autentikasi serta middleware untuk pengamanan API kamu tidak diakses sembarangan oleh orang lain. Silahkan bereksplorasi, apabila kamu ada pertanyaan silahkan tulis di kolom komentar dibawah ini ya ^^.
Apabila pengen ngeclone repo saya silahkan nih
Happy coding & semoga bermanfaat!