API yang menyimpan data di memori (in-memory) akan kehilangan semua data setiap kali server restart. Untuk aplikasi nyata, kita butuh database yang persisten. Di artikel ini kita akan menghubungkan Express.js ke database menggunakan Prisma — ORM (Object-Relational Mapper) modern untuk Node.js yang sangat developer-friendly.
Kita akan menggunakan SQLite sebagai database agar mudah dicoba tanpa setup server database terpisah, namun konsep yang sama berlaku untuk PostgreSQL dan MySQL.
Apa itu Prisma?
Prisma adalah ORM generasi berikutnya untuk Node.js dan TypeScript. Keunggulannya:
- Type-safe — autocomplete dan error saat kompilasi (terutama dengan TypeScript)
- Migrasi otomatis — ubah schema, Prisma urus migrasi database
- Prisma Studio — GUI visual untuk melihat dan mengedit data
- Mendukung PostgreSQL, MySQL, SQLite, SQL Server, MongoDB
Setup Project
mkdir express-prisma
cd express-prisma
npm init -y
npm install express prisma @prisma/client
npx prisma init --datasource-provider sqlite
Perintah prisma init menghasilkan:
- Folder
prisma/dengan fileschema.prisma - File
.envdenganDATABASE_URL
Definisikan Schema
Edit prisma/schema.prisma:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model Pengguna {
id Int @id @default(autoincrement())
nama String
email String @unique
password String
dibuatPada DateTime @default(now())
artikel Artikel[]
}
model Artikel {
id Int @id @default(autoincrement())
judul String
konten String
kategori String @default("umum")
diterbitkan Boolean @default(false)
dibuatPada DateTime @default(now())
diubahPada DateTime @updatedAt
penulis Pengguna @relation(fields: [penulisId], references: [id])
penulisId Int
}
Jalankan migrasi untuk membuat tabel di database:
npx prisma migrate dev --name init
Generate Prisma Client:
npx prisma generate
Buat Prisma Client Instance
prisma/client.js:
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
module.exports = prisma;
Route CRUD dengan Prisma
routes/artikel.js:
const express = require('express');
const router = express.Router();
const prisma = require('../prisma/client');
// GET /artikel
router.get('/', async (req, res, next) => {
try {
const { kategori, q, halaman = 1, limit = 10 } = req.query;
const skip = (parseInt(halaman) - 1) * parseInt(limit);
const where = {};
if (kategori) where.kategori = kategori;
if (q) where.judul = { contains: q };
const [data, total] = await Promise.all([
prisma.artikel.findMany({
where,
skip,
take: parseInt(limit),
orderBy: { dibuatPada: 'desc' },
include: { penulis: { select: { id: true, nama: true } } }
}),
prisma.artikel.count({ where })
]);
res.json({ total, halaman: parseInt(halaman), data });
} catch (err) {
next(err);
}
});
// GET /artikel/:id
router.get('/:id', async (req, res, next) => {
try {
const artikel = await prisma.artikel.findUnique({
where: { id: parseInt(req.params.id) },
include: { penulis: { select: { id: true, nama: true, email: true } } }
});
if (!artikel) return res.status(404).json({ error: 'Artikel tidak ditemukan' });
res.json(artikel);
} catch (err) {
next(err);
}
});
// POST /artikel
router.post('/', async (req, res, next) => {
try {
const { judul, konten, kategori, penulisId } = req.body;
if (!judul || !konten || !penulisId) {
return res.status(422).json({ error: 'Judul, konten, dan penulisId wajib diisi' });
}
const baru = await prisma.artikel.create({
data: { judul, konten, kategori: kategori || 'umum', penulisId: parseInt(penulisId) }
});
res.status(201).json(baru);
} catch (err) {
next(err);
}
});
// PATCH /artikel/:id
router.patch('/:id', async (req, res, next) => {
try {
const diupdate = await prisma.artikel.update({
where: { id: parseInt(req.params.id) },
data: req.body
});
res.json(diupdate);
} catch (err) {
if (err.code === 'P2025') {
return res.status(404).json({ error: 'Artikel tidak ditemukan' });
}
next(err);
}
});
// DELETE /artikel/:id
router.delete('/:id', async (req, res, next) => {
try {
await prisma.artikel.delete({ where: { id: parseInt(req.params.id) } });
res.status(204).send();
} catch (err) {
if (err.code === 'P2025') {
return res.status(404).json({ error: 'Artikel tidak ditemukan' });
}
next(err);
}
});
module.exports = router;
Entry Point Lengkap
index.js:
const express = require('express');
const prisma = require('./prisma/client');
const app = express();
app.use(express.json());
app.use('/api/artikel', require('./routes/artikel'));
// Error handler
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: 'Terjadi kesalahan server' });
});
// Graceful shutdown — tutup koneksi Prisma saat app berhenti
process.on('SIGINT', async () => {
await prisma.$disconnect();
process.exit(0);
});
app.listen(3000, () => {
console.log('Server berjalan di http://localhost:3000');
});
Prisma Studio — GUI Visual
Untuk melihat dan mengedit data secara visual, jalankan:
npx prisma studio
Browser akan terbuka di http://localhost:5555 dengan antarmuka tabel yang bisa kamu gunakan untuk membaca, menambah, dan menghapus data.
Migrasi Saat Schema Berubah
Ketika kamu mengubah schema.prisma (misalnya menambah kolom baru), jalankan:
npx prisma migrate dev --name tambah-kolom-slug
Prisma akan otomatis membuat file migrasi SQL dan menerapkannya ke database.
Kesimpulan
Prisma membuat interaksi dengan database menjadi sangat menyenangkan di Express.js — tidak ada query SQL manual, tidak ada hasil berupa raw array, dan semua operasi async/await yang bersih. Dengan schema yang terdefinisi jelas dan migrasi otomatis, pengelolaan database menjadi jauh lebih mudah.
Di artikel berikutnya, kita tambahkan lapisan keamanan dengan autentikasi JWT di Express.js.
Kiki/🎮🍉⌨️🍩💻
Sharing: 6 Bulan Pakai FlyEnv & Tips Kelola Banyak Project Disatu Site untuk Pengguna