Belajar Fullstack Developer - Fullstack Framework (Next.js)
Episode 3 of 28

Belajar Fullstack Developer - Fullstack Framework (Next.js)

Membangun aplikasi fullstack pertama dengan Next.js App Router: memahami Server Components, Server Actions, dan alur data fullstack — dari formulir di browser sampai baris di database — dalam satu codebase.

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

Pendahuluan

Setelah di episode 2 kita memilih arsitektur (Next.js server-rendered + database), pada episode ini kita menulis kode sungguhan. Ini episode pertama yang benar-benar hands-on — kalian akan membangun aplikasi fullstack kecil bernama catatan (notes) dan merasakan sendiri bagaimana meta-framework meniadakan batas frontend-backend.

Mengapa Next.js? Karena ia adalah meta-framework fullstack paling matang di 2026: App Router sebagai model routing, Server Components sebagai cara rendering, dan Server Actions sebagai jembatan data tanpa menulis REST endpoint. Memahami tiga konsep ini berarti memahami inti dari "fullstack modern".

Membuat Proyek Awal

Gunakan create-next-app — ia menyiapkan struktur, TypeScript, Tailwind, dan ESLint sekaligus:

Buat proyek Next.js
pnpm create next-app@latest catatan-fullstack \
  --typescript --tailwind --eslint --app \
  --src-dir --import-alias "@/*"
cd catatan-fullstack
pnpm dev

Buka http://localhost:3000 — halaman default Next.js akan tampil. Sekarang mari bedah struktur yang terbentuk.

App Router: Routing Berbasis Folder

Pada App Router, setiap folder di dalam src/app adalah sebuah route. Konvensinya sederhana:

Folder/FileFungsi
src/app/page.tsxRoute /
src/app/notes/page.tsxRoute /notes
src/app/notes/[id]/page.tsxRoute dinamis /notes/1, /notes/2
src/app/layout.tsxLayout yang membungkus semua halaman

File layout.tsx penting: ia merender sekali dan dipertahankan saat navigasi antar halaman — tidak seperti model page-per-page klasik:

src/app/layout.tsx
import type { Metadata } from "next"
 
export const metadata: Metadata = {
  title: "Catatan",
  description: "Aplikasi catatan fullstack",
}
 
export default function RootLayout({
  children,
}: Readonly<{ children: React.ReactNode }>) {
  return (
    <html lang="id">
      <body className="max-w-2xl mx-auto p-6">
        <header>
          <h1 className="text-2xl font-bold">Catatan</h1>
        </header>
        <main>{children}</main>
      </body>
    </html>
  )
}

Server Components: Rendering di Server

Konsep kunci Next.js: semua komponen adalah Server Component secara default. Artinya komponen dieksekusi di server — bisa mengakses database, file, dan secrets — lalu hasil HTML-nya dikirim ke browser. JavaScript yang dikirim ke client hanya untuk interaktivitas.

src/app/notes/page.tsx
import { getNotes } from "@/lib/db"
 
export default async function NotesPage() {
  const notes = await getNotes()
  return (
    <ul>
      {notes.map((note) => (
        <li key={note.id}>
          <h2>{note.title}</h2>
          <p>{note.body}</p>
        </li>
      ))}
    </ul>
  )
}

Perhatikan: fungsi async langsung dipanggil di dalam komponen, dan getNotes() membaca dari database di server. Tidak ada fetch, tidak ada loading spinner, tidak ada API. Ini adalah fullstack data flow paling ringkas yang pernah ada.

Kapan Memakai Client Component?

Tambahkan "use client" di baris pertama file hanya saat butuh interaktivitas browser: useState, useEffect, event handler, atau API browser. Aturan praktis: makin sedikit client component, makin cepat aplikasi.

src/app/notes/[id]/counter.tsx
"use client"
 
import { useState } from "react"
 
export default function Counter() {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount(count + 1)}>{count}</button>
}

Server Actions: Menulis Data Tanpa REST

Bagian paling menakjubkan: menulis data ke database tanpa membuat endpoint API. Fungsi "use server" dipanggil langsung dari form HTML. Next.js mengirim request ke server action secara otomatis, menjalankannya, dan merevalidasi tampilan:

src/app/notes/new/page.tsx
import { db } from "@/lib/db"
import { revalidatePath } from "next/cache"
import { redirect } from "next/navigation"
 
async function createNote(formData: FormData) {
  "use server"
  const title = String(formData.get("title") ?? "")
  const body = String(formData.get("body") ?? "")
  const note = await db.note.create({
    data: { title, body },
  })
  revalidatePath("/notes")
  redirect(`/notes/${note.id}`)
}
 
export default function NewNote() {
  return (
    <form action={createNote}>
      <input name="title" required placeholder="Judul" />
      <textarea name="body" required placeholder="Isi" />
      <button type="submit">Simpan</button>
    </form>
  )
}

Alurnya lengkap end-to-end: form di browser → createNote di server → db.note.create → redirect + revalidasi. Satu file, dua sisi, tanpa API layer.

Warning

Server Actions tetap butuh validasi dan proteksi CSRF — keamanan tidak otomatis hilang. Kita bahas validasi di episode 7 dan keamanan CSRF/XSS di episode 16. Untuk sekarang, cukup pahami alurnya.

Alur Data Fullstack dalam Gambar

100%

Inilah lingkaran fullstack yang kita bahas di episode 1 — sekarang kalian melihatnya hidup dalam kode nyata.

Praktik: Aplikasi Catatan

Selesaikan praktik dengan tiga langkah:

  1. Buat halaman /notes yang menampilkan daftar catatan (seperti kode di atas).
  2. Buat halaman /notes/new dengan form yang memanggil server action createNote.
  3. Pastikan getNotes dan db sementara memakai data dummy array (database asli menyusul di episode 4).
src/lib/db.ts
export type Note = { id: string; title: string; body: string }
 
const notes: Note[] = [
  { id: "1", title: "Pertama", body: "Belajar Server Components" },
  { id: "2", title: "Kedua", body: "Belajar Server Actions" },
]
 
export async function getNotes(): Promise<Note[]> {
  return notes
}

Jalankan pnpm dev dan coba tambahkan catatan dari form. Jika berhasil, kalian baru saja membangun fitur fullstack pertama — tanpa menulis satu pun endpoint API.

Penutup

Inti yang harus dibawa pulang:

  • App Router: folder = route, layout.tsx mempertahankan UI antar navigasi.
  • Server Components: default rendering di server, bisa akses database langsung.
  • Server Actions: tulis data dari form tanpa REST endpoint, plus revalidasi otomatis.
  • Client component hanya untuk interaktivitas — jaga seminimal mungkin.
  • Fullstack data flow: form → action → database → revalidate → UI baru.

Di episode 4 selanjutnya kita akan mengganti data dummy dengan database & ORM sungguhan — Prisma/Drizzle, migration, dan query optimization — plus praktik model + CRUD yang akan dipakai semua episode berikutnya. Pastikan PostgreSQL dari episode 0 sudah berjalan, karena kita akan menyambungkannya sekarang!

Belajar Fullstack Developer - Fullstack Framework (Next.js) | Belajar Fullstack