Transaksi berhasil! Pelajari cara mendesain tabel 'Pesanan' dan 'DetailPesanan' di Supabase, lalu membuat API Route atau Server Action di Next.js untuk menyimpan informasi pesanan (item keranjang, data pengguna) ke database.
17m bacaanKuis WajibMicro Badge Lesson
Pesanan Masuk! Nyimpen Data Checkout Toko Kue ke Database Supabase
Toko Kue kita udah punya sistem login dan halaman checkout yang lumayan. Tapi, pas pengguna ngeklik "Proses Pesanan", datanya cuma nongol di alert dan console.log terus keranjangnya dikosongin. Sayang banget kan, pesanan benerannya gak kesimpen di mana-mana!
Nah, di bagian ini, kita bakal bikin proses checkout jadi lebih "beneran" dengan cara nyimpen data pesanan itu ke database Supabase kita. Ini ngelibatin beberapa langkah:
Ngedesain Tabel Database buat nyimpen info pesanan dan detail item yang dipesan.
Bikin API Endpoint (Route Handler) atau Server Action di Next.js buat nerima data checkout dari frontend dan nyimpennya ke tabel-tabel itu pake Supabase Client (atau Prisma Client kalau kita pake itu buat interaksi DB).
Anggap aja kita mau bikin dua tabel utama:
Pesanan: Buat nyimpen info umum soal pesanan (siapa yang pesen, kapan, total harga, status, alamat kirim).
DetailPesanan: Buat nyimpen item-item kue apa aja yang ada di dalem satu pesanan itu (karena satu pesanan bisa isi banyak jenis kue).
Langkah 1: Mendesain Tabel Pesanan dan DetailPesanan di Supabase
Kita bisa bikin tabel ini pake Table Editor di Supabase, atau kalau kamu pake Prisma buat ngelola skema database Supabase-mu, kamu definisiin modelnya di schema.prisma terus prisma migrate dev / db push.
Buat tabel baru bernama detail_pesanan (atau order_items). Kolom-kolomnya:
id: uuid, Primary Key, default gen_random_uuid().
pesanan_id: uuid, Foreign Key ke pesanan.id. Relasi penting!
kue_id: text (atau tipe ID Kue-mu di tabel kue), Foreign Key ke kue.id. Relasi penting!
nama_kue: text (buat referensi cepat, meskipun bisa di-join).
jumlah: integer, NOT NULL.
harga_satuan: integer (harga kue pas dipesen).
subtotal: integer (jumlah * harga_satuan).
created_at: timestamptz, default now(), NOT NULL.
PENTING SOAL RELASI & RLS:
Pastikan kamu ngeset Foreign Key Constraints dengan bener di Supabase Table Editor biar integritas data terjaga.
Pikirin juga Row Level Security (RLS) buat tabel pesanan dan detail_pesanan. Misalnya:
Pengguna yang login cuma boleh INSERT pesanan buat dirinya sendiri (user_id harus sama dengan auth.uid()).
Pengguna yang login cuma boleh SELECT (baca) pesanan miliknya sendiri.
Admin mungkin punya akses lebih luas.
(Bikin RLS ini agak di luar cakupan dasar, tapi penting buat diketahui).
Opsi B: Pake Prisma Schema (Kalau Kamu Pake Prisma buat Kelola DB Supabase)
Tambahin model ini ke prisma/schema.prisma-mu, terus jalanin npx prisma db push (atau migrate dev):
prisma
// prisma/schema.prisma// ... (model User dan Kue yang udah ada) ...model Pesanan { id String @id @default(uuid()) user_id String // Di Supabase, auth.users.id itu UUID (string) user User @relation(fields: [user_id], references: [id]) // Asumsi model User Prisma-mu punya id String juga nama_pemesan String email_pemesan String alamat_pengiriman String telepon_pemesan String? catatan_pesanan String? total_harga Int status_pesanan String @default("PENDING") created_at DateTime @default(now()) updated_at DateTime @updated_at detailPesanan DetailPesanan[] // Satu Pesanan punya banyak DetailPesanan}model DetailPesanan { id String @id @default(uuid()) pesanan Pesanan @relation(fields: [pesananId], references: [id]) pesananId String // Jika kamu punya model Kue di Prisma: kue Kue @relation(fields: [kue_id], references: [id]) kue_id String // Sesuaikan tipe ID Kue-mu // Jika tidak ada model Kue di Prisma, bisa simpan info produk langsung: // nama_kueProduk String // idKueProduk String nama_kue String // Nama kue saat dipesan (bisa juga diambil dari relasi) jumlah Int harga_satuan Int subtotal Int created_at DateTime @default(now())}// Pastikan model User di Prisma juga punya relasi ke Pesanan// model User {// // ... field lain ...// pesanan Pesanan[]// }
Catatan: Kalau pake Prisma, ID User dari Supabase Auth (auth.uid()) itu tipenya uuid (string). Jadi, field user_id di model Pesanan Prisma-mu dan id di model User Prisma-mu harusnya juga string (atau uuid kalau databasemu ngedukung tipe uuid langsung di Prisma dan kamu mau pake itu). Sesuaikan relasinya.
Pilih salah satu cara buat bikin tabelnya. Buat contoh ini, kita asumsikan tabelnya udah jadi di Supabase.
Langkah 2: Bikin API Route (Route Handler) buat Nyimpen Pesanan
Kita butuh API endpoint di Next.js (misal, /api/checkout) yang bakal nerima data checkout dari frontend, terus nyimpennya ke tabel pesanan dan detail_pesanan di Supabase.
Di src/app/api/, bikin folder checkout.
Di dalemnya, bikin file route.ts.
File src/app/api/checkout/route.ts:
typescript
// src/app/api/checkout/route.tsimport { NextResponse, NextRequest } from 'next/server';import { supabase } from '@/lib/supabase-client'; // Supabase client kitaimport { ItemKeranjang, Kue } from '@/data/kue'; // Tipe data kita// Tipe data buat request body dari frontendinterface CheckoutRequestBody { nama: string; email: string; alamat: string; telepon?: string
Bedah API Route /api/checkout/route.ts:
Dia cuma nanganin method POST.
CheckoutRequestBody: Kita definisiin tipe buat data yang kita harapin dari frontend.
Ambil user_id: Ini bagian yang agak tricky.
Cara paling aman adalah ngambil user_id dari sesi server-side yang valid. Supabase nyediain helper buat ini (misal, pake @supabase/ssr atau createRouteHandlerClient kalau pake Pages Router lama). Di App Router, kamu bisa bikin Supabase client khusus buat Route Handler yang bisa akses info sesi dari cookies.
Untuk contoh dasar ini, saya menyederhanakannya dengan asumsi user_id bisa didapat dari supabase.auth.getSession(). Namun, dalam skenario Route Handler yang dipanggil dari client, sesi ini mungkin tidak selalu tersedia langsung seperti di Client Component. Anda mungkin perlu setup Supabase Auth Helpers for Next.js (@supabase/auth-helpers-nextjs) atau @supabase/ssr untuk manajemen sesi yang robust di server-side (Route Handlers).
SANGAT PENTING: Jangan pernah percaya user_id yang dikirim dari body request klien tanpa validasi server-side yang ketat terhadap sesi aktif!
Pada contoh di atas, saya menambahkan ...(user_id && { user_id: user_id }) agar user_id hanya ditambahkan jika user_id ada nilainya. Skema database Anda di kolom pesanan.user_id harus memperbolehkan NULL jika Anda ingin mendukung guest checkout atau jika sesi tidak selalu ada.
Insert ke pesanan: Kita pake supabase.from('pesanan').insert(...).select().single(). .select().single() penting biar kita dapet idPesananBaru yang di-generate Supabase.
Insert ke detail_pesanan: Kita nge-map itemDipesan dari body jadi array objek yang formatnya sesuai buat tabel detail_pesanan, terus kita insert semua sekaligus.
Error Handling: Ada blok try...catch buat nangkep error. Idealnya, kalau insert detail gagal, kita harusnya nge-rollback (hapus) record pesanan utama yang udah keburu kebuat. Ini namanya transaksi, yang lebih advance. Buat sekarang, kita cukup lempar error.
Response Sukses: Kirim balik pesan sukses dan ID pesanan baru dengan status 201 Created.
Langkah 3: Modifikasi Fungsi handleSubmitCheckout di Frontend
Sekarang, kita ubah fungsi handleSubmitCheckout di src/app/checkout/page.tsx biar dia manggil API endpoint /api/checkout kita pake fetch.
File src/app/checkout/page.tsx (bagian handleSubmitCheckout dan state terkait):
tsx
// src/app/checkout/page.tsx"use client";import React, { useState, FormEvent, useEffect } from 'react';import Link from 'next/link';import { useRouter } from 'next/navigation';import { useCart } from '@/context/cart-context';export default function HalamanCheckout() { const router = useRouter(); const { itemDiKeranjang,
Perubahan di HalamanCheckout.tsx (Submit Logic):
State form diubah jadi satu objek formData biar lebih gampang di-manage.
handleInputChange sekarang generik, bisa ngupdate field mana aja berdasarkan atribut name.
handleSubmitCheckout sekarang jadi async dan manggil fetch('/api/checkout', ...) pake method POST.
Body request di-JSON.stringify() dan isinya formData, itemDipesan dari context, dan total_harga.
Ada penanganan response ok atau error dari API.
State isSubmitting dan submitError ditambahin buat UX yang lebih baik.
Input fields di JSX diupdate value dan onChange-nya biar ngacu ke formData dan handleInputChange.
Tes Lagi!
Sekarang, kalau kamu jalanin semua ini:
Pastikan tabel pesanan dan detail_pesanan udah ada di Supabase-mu (dan RLS-nya ngizinin insert, atau sementara di-disable dulu RLS-nya buat tes).
Login ke Toko Kue.
Masukin item ke keranjang.
Lanjut ke Checkout.
Isi form, klik "Proses Pesanan".
Kalau semua bener, kamu bakal liat alert sukses, diarahkan ke halaman terima kasih, dan kalau kamu cek di Supabase Table Editor, data pesanan dan detail pesanannya udah masuk!
Luar biasa! Toko Kue Online kita sekarang udah bener-bener bisa nerima dan nyimpen pesanan! Kita udah ngeliat gimana Next.js App Router (Server Components & API Routes), React (Client Components, State, Context), TypeScript, dan Supabase (Database & Auth) bisa kerja bareng buat bikin aplikasi full-stack sederhana.
Ini adalah puncak dari studi kasus kita. Dari sini, banyak banget yang bisa kamu kembangin lagi.
Checkpoint KuisTarget: 70%
Kuis Menyimpan Pesanan ke Database Supabase (Toko Kue)
Kuis ini wajib untuk sertifikat. Kamu tetap bisa lanjut ke pelajaran berikutnya dan mengerjakannya nanti.