Belajar Remix - Database & Persistence
Episode 10 of 24

Belajar Remix - Database & Persistence

Episode ini membahas penyimpanan data di Remix: integrasi ORM seperti Prisma, setup connection pooling dengan konfigurasi berbasis environment, querying di loader dan action, serta transaction handling untuk operasi yang aman.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

Hingga episode 9, semua data kalian hidup dalam array dan objek yang hilang saat server restart. Aplikasi serius butuh persistence: data tersimpan di database dan bertahan di luar proses. Di sinilah Remix menunjukkan kekuatannya, karena query database ditulis langsung di loader dan action — di server.

Pendekatan ini menghilangkan lapisan API tambahan. Kalian tidak perlu membangun REST endpoint khusus untuk data internal; loader cukup memanggil fungsi repository, dan action menyimpan perubahan. Hasilnya kode lebih sedikit, jenis lebih aman, dan alur lebih mudah diikuti.

Episode 10 akan memakai Prisma sebagai ORM contoh, lalu membahas koneksi dan pooling, querying di loader dan action, serta transaction handling.

Integrasi Prisma

Setup Prisma

Prisma adalah ORM TypeScript paling populer di ekosistem Remix. Install lalu inisialisasi dengan driver PostgreSQL atau SQLite untuk belajar:

Install dan inisialisasi Prisma
npm install prisma @prisma/client
npx prisma init --datasource-provider postgresql

Perintah prisma init membuat folder prisma dan file .env untuk DATABASE_URL. Skema database ditulis dalam Prisma Schema Language, bukan SQL manual.

Mendefinisikan Model

Model didefinisikan di prisma/schema.prisma, lalu disinkronkan ke database:

Model Post di schema.prisma
model Post {
  id        String   @id @default(cuid())
  judul     String
  konten    String
  createdAt DateTime @default(now())
}

Setelah model ditulis, jalankan prisma migrate dev untuk membuat tabel. Prisma menghasilkan tipe TypeScript yang bisa dipakai langsung di loader dan action.

Koneksi dan Connection Pooling

Pola Singleton PrismaClient

Saat development, hot reload membuat banyak instance PrismaClient jika tidak ditangani. Solusi standar adalah singleton global:

JSPrismaClient singleton
import { PrismaClient } from "@prisma/client";
 
const globalForPrisma = globalThis;
export const prisma = globalForPrisma.prisma ?? new PrismaClient();
 
if (process.env.NODE_ENV !== "production") {
  globalForPrisma.prisma = prisma;
}

Pola ini memastikan satu instance PrismaClient dipakai ulang selama process hidup. Ini mencegah pembocoran koneksi di development dan mempercepat startup.

Connection Pooling

Untuk serverless yang memulai process baru terus-menerus, setiap process membuat koneksi baru — berisiko menghabiskan batas koneksi database. Gunakan connection pooler seperti PgBouncer atau Supabase Pooler. Atur pool size sesuai env dengan env var seperti DATABASE_POOL_SIZE agar development dan production memakai nilai berbeda.

Querying di Loader dan Action

Membaca Data di Loader

Loader adalah tempat query baca. Data dikembalikan langsung ke komponen:

JSQuery Prisma di loader
import { prisma } from "~/lib/prisma.server";
import { useLoaderData } from "@remix-run/react";
 
export async function loader() {
  const posts = await prisma.post.findMany({
    orderBy: { createdAt: "desc" },
    take: 10,
  });
  return { posts };
}
 
export default function Daftar() {
  const { posts } = useLoaderData();
  return (
    <ul>
      {posts.map((p) => <li key={p.id}>{p.judul}</li>)}
    </ul>
  );
}

Loader memanggil prisma.post.findMany langsung di server. Tidak ada endpoint API perantara — query dan rendering hidup di satu file route.

Action menulis perubahan. Dengan idempotency dan validasi sebelum menulis:

JSMenyimpan data dari action
export async function action({ request }) {
  const formData = await request.formData();
  const judul = String(formData.get("judul"));
 
  const post = await prisma.post.create({
    data: { judul },
  });
 
  return redirect(`/posts/${post.id}`);
}

Prisma.create mengembalikan data yang baru dibuat, lalu redirect membawa pengguna ke detail. Karena loader route di-refresh otomatis setelah action, daftar posting selalu sinkron tanpa kode tambahan.

Transaction dan Persistence

Operasi Atomik dengan $transaction

Saat satu aksi harus mengubah beberapa tabel secara bersamaan, gunakan transaction agar semua berhasil atau semuanya gagal. Prisma.$transaction menjalankan beberapa operasi secara atomik.

JSTransaction dengan $transaction
export async function action({ request }) {
  const formData = await request.formData();
  const postId = String(formData.get("postId"));
 
  await prisma.$transaction([
    prisma.viewLog.create({ data: { postId } }),
    prisma.post.update({ where: { id: postId }, data: { dilihat: { increment: 1 } } }),
  ]);
 
  return redirect(`/posts/${postId}`);
}

Array operasi di $transaction dieksekusi bersama; jika satu gagal, semuanya dibatalkan. Ini penting untuk data yang harus konsisten, seperti counter dan log.

Batasi yang Dikirim ke Client

Loader mengembalikan semua field model secara default. Jangan kirim data sensitif ke client. Pilih field dengan select di Prisma, atau buat fungsi serializer di modul tersendiri. Praktik ini akan dibahas lagi di episode 13 tentang keamanan dan episode 18 tentang arsitektur.

Penutup

Episode 10 menghubungkan aplikasi kalian dengan database: Prisma untuk ORM dan schema, singleton PrismaClient dengan connection pooling, querying langsung di loader dan action, serta transaction untuk konsistensi data. Persistence kini bukan lagi kata asing.

Inti yang harus dibawa pulang:

  • Query database ditulis langsung di loader dan action, tanpa lapisan API internal.
  • Prisma mendefinisikan skema dan menghasilkan tipe TypeScript.
  • Pakai pola singleton untuk PrismaClient di development.
  • Connection pooler penting untuk beban serverless.
  • Action menulis dengan validasi; redirect setelah data tersimpan.
  • $transaction membuat beberapa operasi berjalan atomik.

Di episode 11 selanjutnya kita akan membahas authentication dan session — pola autentikasi di Remix, session handling dengan cookies, proteksi route dan user context, serta OAuth dan social login dengan sesi yang aman. Database sudah menyimpan data; sekarang saatnya memastikan hanya orang yang berhak yang mengaksesnya.

Belajar Remix - Database & Persistence | Belajar Remix