Jika Anda terbiasa dengan Laravel, konsep Next.js awalnya bisa terasa asing. Tapi jika Anda bisa melihat analoginya, banyak konsep yang sebenarnya familiar: routing berbasis file, server-side rendering, middleware, dan API routes. Artikel ini menjelaskan Next.js 15 dari perspektif seorang backend developer yang terbiasa dengan paradigma MVC.

Analogi Next.js → Laravel

Next.jsLaravel Equivalent
Server ComponentsBlade templates dengan data dari controller
Client ComponentsJavaScript di view (Alpine.js, Livewire)
API Routes (route.ts)Controller dengan resource routes
Middleware (middleware.ts)Laravel Middleware
layout.tsxBlade layout (@extends)
loading.tsxSkeleton loading component
error.tsxError handler / 500 page
Server ActionsForm submission ke controller

App Router: File-based Routing

Next.js App Router menggunakan struktur folder sebagai routing:

app/
├── page.tsx              → /
├── layout.tsx            → Root layout (seperti app.blade.php)
├── blog/
│   ├── page.tsx          → /blog
│   └── [slug]/
│       └── page.tsx      → /blog/artikel-saya
├── api/
│   └── posts/
│       ├── route.ts      → GET/POST /api/posts
│       └── [id]/
│           └── route.ts  → GET/PUT/DELETE /api/posts/:id
└── (admin)/              → Route group (tidak affect URL)
    └── dashboard/
        └── page.tsx      → /dashboard

Server Components: Fetch Data Langsung di Component

Ini adalah konsep paling signifikan di Next.js modern. Server Components berjalan di server, bisa langsung query database atau fetch API, dan hasilnya di-render ke HTML. Mirip controller + Blade di Laravel:

// app/blog/page.tsx — Ini SERVER component by default
// Tidak perlu useEffect atau useState untuk fetch data awal

async function getBlogPosts() {
    const res = await fetch('https://api.example.com/posts', {
        next: { revalidate: 3600 }, // Cache 1 jam, revalidate otomatis
    });
    return res.json();
}

export default async function BlogPage() {
    const { data: posts } = await getBlogPosts();

    return (
        <main>
            <h1>Blog</h1>
            <ul>
                {posts.map((post) => (
                    <li key={post.id}>
                        <a href={`/blog/${post.slug}`}>{post.title}</a>
                    </li>
                ))}
            </ul>
        </main>
    );
}

API Routes: Backend dalam Next.js

Next.js bisa berfungsi sebagai backend ringan via Route Handlers:

// app/api/posts/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function GET(request: NextRequest) {
    const { searchParams } = new URL(request.url);
    const page = Number(searchParams.get('page') ?? '1');

    const posts = await db.post.findMany({
        skip: (page - 1) * 15,
        take: 15,
        orderBy: { publishedAt: 'desc' },
    });

    return NextResponse.json({ data: posts });
}

export async function POST(request: NextRequest) {
    const body = await request.json();

    // Validasi dengan Zod
    const schema = z.object({
        title: z.string().min(1).max(255),
        content: z.string().min(10),
    });

    const result = schema.safeParse(body);
    if (!result.success) {
        return NextResponse.json({ errors: result.error.issues }, { status: 422 });
    }

    const post = await db.post.create({ data: result.data });
    return NextResponse.json(post, { status: 201 });
}

Server Actions: Form Submission Tanpa API Route

Server Actions mirip konsep form POST ke controller Laravel, tapi lebih terintegrasi:

// Di Server Component
async function createPost(formData: FormData) {
    'use server'; // Tandai ini adalah server action

    const title = formData.get('title') as string;
    const content = formData.get('content') as string;

    await db.post.create({ data: { title, content } });
    revalidatePath('/blog');
    redirect('/blog');
}

export default function NewPostPage() {
    return (
        <form action={createPost}>
            <input name="title" placeholder="Judul" required />
            <textarea name="content" placeholder="Konten" required />
            <button type="submit">Publish</button>
        </form>
    );
}

Middleware: Auth Guard

// middleware.ts (di root project)
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { getToken } from 'next-auth/jwt';

export async function middleware(request: NextRequest) {
    const token = await getToken({ req: request });

    if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
        return NextResponse.redirect(new URL('/login', request.url));
    }

    return NextResponse.next();
}

export const config = {
    matcher: ['/dashboard/:path*', '/api/admin/:path*'],
};

Kapan Pakai Next.js vs Laravel?

  • Next.js: Proyek JavaScript-first, butuh SSR/SSG untuk SEO, tim front-end yang dominan TypeScript
  • Laravel + Inertia.js: Tim PHP, butuh semua fitur framework backend (Eloquent, Queue, Mail), atau convert dari Laravel yang sudah ada
  • Laravel API + Next.js: Ketika butuh kekuatan backend Laravel tapi frontend yang lebih modern

Next.js 15 dengan App Router adalah framework yang sangat mature dan siap production. Bagi backend developer, memahami Server Components dan Server Actions adalah kunci — setelah "klik", Anda akan merasa seperti menulis controller PHP tapi dalam JavaScript.