Mengimplementasikan Operasi CRUD dengan Hapi JS dan PostgreSQL
Dalam pengembangan aplikasi, mengelola data dan menyimpannya dalam database merupakan hal yang sangat penting. Operasi CRUD (Create, Read, Update, Delete) merupakan operasi dasar yang digunakan untuk mengelola data dalam database. Operasi CRUD dapat diterapkan dengan menggunakan berbagai framework dan database.
Pada artikel sebelumnya, Menerapkan Plugin dan Routing Pada Aplikasi Hapi JS, kita telah berhasil membuat project Hapi menjadi modular dan membuat routing. Pada artikel ini, akan dibahas tentang bagaimana mengimplementasikan operasi CRUD menggunakan Hapi JS dan PostgreSQL.
Tools yang digunakan dalam project
Berikut adalah tools tambahan yang perlu dipersiapkan:
- PostgreSQL
PostgreSQL adalah database relational berfungsi sebagai lokasi penyimpanan data. PostgreSQL dapat diunduh pada laman berikut: https://www.postgresql.org/download/ - Postman
Postman berfungsi sebagai REST Client untuk menajalankan operasi CRUD. Postman dapat diunduh pada laman berikut: https://www.postman.com/downloads/
Deskripsi project dan pengenalan endpoint
Project ini merupakan lanjutan dari artikel sebelumnya, Silakan buka repositori Github untuk melihat kode sebelumnya.
Pada project ini akan dibuat tabel orders
pada database PostgreSQL dan operasi CRUD yang akan dibuat adalah sebagai berikut:
Membuat project dan menginstall modul
Silakan buka terminal lalu jalankan perintah berikut:
npm i @hapi/boom dotenv pg sequelize
Perintah diatas berfungsi untuk menginstall modul berikut:
@hapi/boom
: mengembalikan HTTP error untuk framework Hapi JSdotenv
: mengelola konfigurasi aplikasi Node JS, biasanya digunakan untuk menyimpan informasi rahasiapg
: modul untuk mengakses PostgreSQL database di Node JSsequelize
: ORM (Object-Relational Mapping) untuk Node.js yang mendukung berbagai jenis database seperti PostgreSQL
Kemudian masuk ke dalam folder src
dan buatlah folder controller
, config
, dan models
. Berikut adalah susunan foldernya:
- src
- config
- controller
- models
- routes
- route.js
- server.js
Selanjutnya buatlah file berikut pada folder yang tersedia:
- pada folder
config
buatlah fileconfig.js
- pada folder
controller
buatlah fileorder.controller.js
- pada folder models buatlah file
index.js
danorders.js
Untuk sementara, biarkan file tetap kosong.
Setelah file berhasil dibuat maka susunan folder akan menjadi seperti berikut:
- src
- config
- config.js
- controller
- order.controller.js
- models
- index.js
- orders.js
- routes
- route.js
- server.js
Membuat username dan database pada PostgreSQL
Silakan buka terminal, lalu login menggunakan username default dari PostgreSQL yaitu postgres dengan mengetik perintah berikut:
psql postgres postgres
Buat username baru dengan perintah berikut:
CREATE ROLE admintutorial WITH LOGIN PASSWORD 'tutorial';
ALTER ROLE admintutorial CREATEDB ;
\q
Setelah user berhasil dibuat, silakan login dan buat database dengan perintah berikut:
psql -U admintutorial -d postgres
CREATE DATABASE hapi_rest_api;
\q
Melakukan konfigurasi database
Di dalam root folder buatlah file .env
, buka file tersebut lalu ketikkan kode berikut:
DB_NAME = "hapi_rest_api"
DB_USER = "admintutorial"
DB_PASSWORD = "tutorial"
DB_HOST = "127.0.0.1"
DB_PORT = "5432"
- File
.env
diatas berisi konfigurasi database yang akan kita gunakan. Karena file.env
berisi informasi sensitif, maka file.env
harus disimpan secara aman dan tidak boleh diunggah ke repositori git publik. Kita perlu menambahkannya ke dalam file.gitignore
. - File
.gitignore
digunakan untuk menyimpan daftar file dan folder yang tidak ingin dimasukkan ke dalam repositori git.
Silakan buat file .gitignore
pada root direktori, dan masukkan kode berikut:
node_modules
.env
Selanjutnya, silakan buka file config.js
pada folder config
, kemudian ketikkan code berikut:
require("dotenv").config();
module.exports = {
HOST: process.env.DB_HOST,
USER: process.env.DB_USER,
PASSWORD: process.env.DB_PASSWORD,
DB: process.env.DB_NAME,
dialect: 'postgres',
}
require(“dotenv”).config()
berfungsi untuk melakukan konfigurasi environment variable dari file .env
ke dalam process.env
.
Mendefinisikan model dan membuat koneksi ke database
Selanjutnya, silakan buka file orders.js
pada folder models
, lalu ketikkan kode berikut:
module.exports = (sequelize, DataTypes) => {
const Order = sequelize.define("orders", {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
product_name: {
type: DataTypes.STRING,
allowNull: false,
},
order_date: {
type: DataTypes.DATE,
allowNull: false,
},
amount: {
type: DataTypes.INTEGER,
allowNull: false,
},
});
return Order;
};
Kode diatas berfungsi untuk menentukan struktur dan tipe data dari kolom pada tabel orders
. Kode diatas mengembalikan objek Order
yang akan merepresentasikan model orders
sehingga model ini dapat diterapkan untuk melakukan operasi CRUD.
Selanjutnya, silakan buka file index.js
pada folder models
, lalu ketikkan kode berikut:
const dbConfig = require("../config/config");
const { Sequelize, DataTypes } = require("sequelize");
const sequelize = new Sequelize(dbConfig.DB, dbConfig.USER, dbConfig.PASSWORD, {
host: dbConfig.HOST,
dialect: dbConfig.dialect,
operatorsAliases: false,
});
sequelize
.authenticate()
.then(() => {
console.log("Connected to database!");
})
.catch((err) => {
console.log("Error" + err);
});
const db = {};
db.Sequelize = Sequelize;
db.sequelize = sequelize;
db.orders = require("./orders.js")(sequelize, DataTypes);
db.sequelize.sync({ force: false }).then(() => {
console.log("re-sync done!");
});
module.exports = db;
- Pada file
index.js
dilakukan koneksi ke database menggunakan fungsi dari sequelize yaitunew Sequelize()
dengan menggunakan konfigurasi yang telah diimpor sebelumnya - Selanjutnya dilakukan autentikasi konesi ke database menggunakan
sequelize.authenticate()
. const db = {}
berfungsi untuk membuat objekdb
yang kemudian akan diisi dengan model sequelize.db.orders = require(“./orders.js”)(sequelize, DataTypes)
berfungsi untuk menambahkan model orders yang telah dibuatdb.sequelize.sync({ force: false })
berfungsi untuk melakukan sinkronisasi model dengan database- di akhir kode, objek
db
diekspor agar dapat digunakan diseluruh aplikasi
Membuat controller untuk operasi CRUD
Buka file order.controller.js
pada folder controller
, lalu tambahkan kode berikut:
const db = require("../models");
const Boom = require("@hapi/boom");
const Orders = db.orders;
const OrderController = {
async addOrder(request, h) {
try {
const { product_name, order_date, amount } = request.payload;
const order = await Orders.create({ product_name, order_date, amount });
return h.response(order).code(201);
} catch (err) {
return Boom.internal(err.message);
}
},
async getOrder(request, h) {
try {
const order = await Orders.findAll({
attributes: ["id", "product_name", "order_date", "amount"],
});
return h.response(order).code(200);
} catch (err) {
return Boom.internal(err.message);
}
},
async getOrderById(request, h) {
try {
const { id } = request.params;
const order = await Orders.findByPk(id, {
attributes: ["id", "product_name", "order_date", "amount"],
});
if (!order) {
return Boom.notFound(`Order with id ${id} not found`);
} else {
return h.response(order).code(200);
}
} catch (err) {
return Boom.internal(err.message);
}
},
async udpateOrder(request, h) {
try {
const { id } = request.params;
const order = await Orders.findByPk(id);
if (!order) {
return Boom.notFound(`Order with id ${id} not found`);
} else {
const { product_name, order_date, amount } = request.payload;
await Orders.update(
{ product_name, order_date, amount },
{ where: { id } }
);
return "Successfully Updated";
}
} catch (err) {
return Boom.internal(err.message);
}
},
async deleteOrder(request, h) {
try {
const { id } = request.params;
const order = await Orders.findByPk(id);
if (!order) {
return Boom.notFound(`Order with id ${id} not found`);
} else {
await Orders.destroy({ where: { id } });
return "Successfully Deleted";
}
} catch (err) {
return Boom.internal(err.message);
}
},
};
module.exports = OrderController;
Pada kode diatas terdapat beberapa fungsi yang ada di dalam objek OrderController, antara lain :
addOrder()
: berfungsi untuk menambahkan data order baru ke database.getOrder()
: berfungsi untuk mengambil semua data order dari database dan mengembalikannya dengan atribut"id"
,"product_name"
,"order_date"
, dan"amount"
.getOrderById()
: berfungsi untuk mengambil data order berdasarkan ID dan mengembalikannya dengan atribut"id"
,"product_name"
,"order_date"
, dan"amount"
.udpateOrder()
: berfungsi untuk memperbarui data order berdasarkan ID.deleteOrder()
: berfungsi untuk menghapus data order berdasarkan ID.
Setiap fungsi menggunakan async/await
untuk menangani operasi yang bersifat asinkron. Fungsi tersebut juga menggunakan library @hapi/boom
untuk menangani kesalahan atau error. Controller ini diekspor dan akan digunakan pada route.
Menambahkan route
Setelah controller berhasil dibuat, silakan tambahkan route dengan memasukkan kode berikut pada file route.js
:
const OrderController = require("../controller/order.controller");
const routes = [
{
method: "GET",
path: "/",
handler: (request, h) => {
return "Hello World !";
},
},
{
method: "GET",
path: "/{any*}",
handler: (request, h) => {
return "Oops! You must be lost!";
},
},
{
method: "POST",
path: "/api/orders",
handler: OrderController.addOrder,
},
{
method: "GET",
path: "/api/orders",
handler: OrderController.getOrder,
},
{
method: "GET",
path: "/api/orders/{id}",
handler: OrderController.getOrderById,
},
{
method: "PUT",
path: "/api/orders/{id}",
handler: OrderController.udpateOrder,
},
{
method: "DELETE",
path: "/api/orders/{id}",
handler: OrderController.deleteOrder,
},
];
module.exports = {
name: "routes",
version: "1.0.0",
register: async (server, options) => {
server.route(routes);
},
};
Pada kode diatas, handler untuk setiap rute menggunakan controller yang telah diimpor sebelumnya, yaitu OrderController
. Controller ini berisi beberapa fungsi yang akan dijalankan ketika rute yang terkait dipanggil. Prefix “/api/”
pada path bertujuan untuk memberi tanda bahwa endpoint tersebut terkait dengan API (Application Programming Interface) dan juga merupakan salah satu penerapan best practice.
Melakukan testing dengan Postman
Jalankan aplikasi dengan perintah berikut:
npm start
Selanjutnya untuk mencoba operasi CRUD, silakan buka aplikasi Postman dan buatlah new collection seperti berikut:
Setelah collection berhasil dibuat, tampilan new collection akan seperti berikut:
Silakan tentukan nama collection dan tambahkan request, kemudian masukkan request URL serta body (jika diperlukan). Untuk melakukan tes silakan klik tombol send. Berikut adalah hasil testing:
- Add Order
- Get Order
- Get Order By Id
- Update Order
- Delete Order
Selamat!!! dengan demikian kamu telah berhasil menerapkan operasi CRUD menggunakan Hapi JS dan PostgreSQL!
Sumber:
https://www.dewaweb.com/blog/apa-itu-crud/
https://sequelize.org/docs/v6/core-concepts/model-basics/
Silakan kunjungi repositori Github untuk melihat seluruh kode yang telah dibahas dalam artikel ini.
Ingin mengetahui lebih banyak? Jangan lewatkan artikel selanjutnya yang akan membahas topik Menarik tentang Menerapkan Pagination Pada Aplikasi Hapi JS.