Jika Anda membangun aplikasi dengan Next.js, pertanyaan yang sering muncul bukan hanya "bisa pakai Route Handler?", tetapi "sampai kapan itu masih masuk akal?". Pada banyak proyek, API Route/Route Handler di App Router cukup sebagai backend langsung untuk kebutuhan CRUD, agregasi ringan, dan orkestrasi sederhana. Namun ketika kompleksitas bisnis, jumlah integrasi, kebutuhan observability, atau ownership tim bertambah, BFF terpisah sering menjadi pilihan yang lebih stabil dalam jangka panjang.
Artikel ini membahas trade-off BFF terpisah vs API Route di App Router secara praktis: kapan pendekatan terintegrasi sudah cukup, kapan service terpisah lebih tepat, apa risiko over-engineering, dan bagaimana migrasi bertahap tanpa rewrite besar. Fokusnya adalah keputusan arsitektur, bukan perdebatan fitur rendering.
Apa yang dibandingkan?
Supaya tidak rancu, kita bedakan dua pendekatan berikut:
1. API Route/Route Handler di Next.js App Router sebagai backend langsung
Pada model ini, aplikasi Next.js menangani UI sekaligus endpoint backend melalui Route Handler. Ia bisa melakukan validasi, memanggil database atau service lain, lalu mengembalikan JSON ke client atau ke komponen server.
Karakteristik umumnya:
- Satu repositori, satu pipeline, sering kali satu deployment unit.
- Cocok untuk kebutuhan backend yang dekat dengan UI.
- Developer frontend dapat bergerak cepat tanpa menunggu service terpisah.
2. BFF/service terpisah dari Next.js
Pada model ini, Next.js berperan sebagai frontend, sedangkan kebutuhan backend di-handle oleh service terpisah. Service itu bisa berupa BFF khusus untuk channel web, atau service backend yang lebih umum untuk berbagai consumer.
Karakteristik umumnya:
- Deployment frontend dan backend terpisah.
- Observability, scaling, dan lifecycle backend bisa dikelola lebih mandiri.
- Cocok saat logika integrasi dan bisnis mulai menjadi domain yang berdiri sendiri.
Catatan penting: BFF tidak selalu berarti microservice penuh. Dalam praktik, BFF bisa berupa satu service tipis yang mengagregasi beberapa upstream API untuk kebutuhan web app tertentu.
Kapan API Route di App Router sudah cukup?
Pendekatan terintegrasi biasanya cukup jika backend Anda masih dekat dengan kebutuhan presentasi dan belum berkembang menjadi platform sendiri.
Sinyal bahwa pendekatan terintegrasi masih tepat
- Tim kecil dan belum ada pemisahan ownership frontend-backend yang jelas.
- Use case sederhana: CRUD, validasi form, upload metadata, agregasi ringan dari satu atau dua upstream.
- Satu consumer utama, yaitu web app yang sama.
- Perubahan cepat lebih penting daripada isolasi arsitektur.
- Biaya operasional rendah menjadi prioritas.
Contoh: dashboard internal
Untuk dashboard internal seperti admin panel operasional, tim sering butuh kecepatan implementasi lebih dari fleksibilitas arsitektur jangka panjang. Route Handler biasanya cukup untuk:
- menerima filter dari UI,
- mengambil data dari database atau satu service internal,
- menggabungkan hasilnya dalam bentuk yang siap dipakai tabel atau chart.
Dalam konteks ini, memisahkan BFF terlalu dini sering hanya menambah overhead: repo baru, pipeline baru, monitoring baru, dan koordinasi tim yang belum tentu memberikan nilai nyata.
Keuntungan nyata pendekatan terintegrasi
- Kecepatan delivery: UI dan endpoint berkembang bersama.
- Konteks lebih dekat: developer memahami kebutuhan data dari layar yang sedang dibangun.
- Operasional lebih sederhana: lebih sedikit service untuk di-deploy dan dipantau.
- Biaya awal lebih rendah: cocok untuk MVP, internal tools, atau produk tahap awal.
Keterbatasan yang mulai terasa
- Logika bisnis bercampur dengan concern UI.
- Endpoint makin banyak dan tidak lagi jelas batas domainnya.
- Reuse untuk consumer lain, misalnya mobile atau partner integration, menjadi tidak natural.
- Observability backend kurang rapi karena lifecycle aplikasi lebih berpusat pada web.
- Beberapa pekerjaan backend yang berat atau lama mulai tidak cocok dijalankan dekat layer frontend.
Kapan BFF terpisah lebih masuk akal?
BFF terpisah vs API Route di App Router mulai condong ke BFF saat kebutuhan backend bukan lagi sekadar pelengkap UI, tetapi sudah menjadi lapisan orkestrasi dan domain logic yang signifikan.
Sinyal bahwa BFF/service terpisah layak dipertimbangkan
- Banyak upstream API dengan format, SLA, dan pola error berbeda.
- Logika agregasi kompleks, termasuk fallback, retry terkontrol, transformasi besar, atau normalisasi respons.
- Kebutuhan observability matang: tracing, metrics per dependency, error taxonomy, audit operasional.
- Ownership tim terpisah: frontend dan backend punya roadmap, ritme, dan tanggung jawab berbeda.
- Skala traffic atau beban kerja berbeda antara web app dan backend API.
- Konsumsi multi-channel: web, mobile, partner, automation, atau worker internal.
Contoh: SaaS multi-tenant
Pada SaaS multi-tenant, satu request sering melibatkan konteks tenant, feature flag, billing state, policy, dan data dari beberapa service. Jika semua ini diletakkan di Route Handler, aplikasi Next.js mudah menjadi terlalu gemuk. BFF terpisah membantu karena:
- kontrak API bisa distabilkan untuk berbagai layar,
- tenant-aware logic bisa dipusatkan,
- instrumentasi per tenant atau per upstream lebih mudah diatur,
- frontend tidak perlu menanggung seluruh kompleksitas orkestrasi.
Contoh: integrasi banyak upstream API
Bayangkan aplikasi harus menggabungkan data dari CRM, billing provider, analytics API, dan service internal. Masing-masing punya timeout, pagination, rate limit, dan bentuk error berbeda. Pada titik ini, memisahkan BFF sering lebih sehat karena:
- retry dan circuit-breaking lebih mudah dipikirkan sebagai concern backend,
- mapping error dan fallback bisa distandardkan,
- Next.js tidak menjadi tempat semua integrasi menumpuk tanpa batas.
Trade-off arsitektur yang paling penting
1. Skalabilitas
API Route/Route Handler lebih sederhana saat beban masih moderat dan pola akses sejalan dengan web app. Namun scaling frontend dan backend menjadi terikat. Jika lonjakan traffic berasal dari endpoint tertentu atau pekerjaan agregasi berat, Anda tidak selalu ingin menskalakan seluruh aplikasi web untuk ikut menanggungnya.
BFF terpisah memberi fleksibilitas untuk menskalakan backend sesuai karakter bebannya. Ini berguna jika ada endpoint tertentu yang jauh lebih sibuk, integrasi upstream lambat, atau kebutuhan throughput backend tidak sebanding dengan traffic UI.
2. Biaya operasional
Pendekatan terintegrasi biasanya paling hemat di awal: satu codebase, satu observability stack minimal, satu alur deployment. Ini mengurangi biaya koordinasi dan kognitif.
BFF terpisah menambah biaya tetap: service baru, CI/CD baru, logging/tracing baru, kebijakan konfigurasi baru, kemungkinan dokumentasi kontrak API, dan tanggung jawab on-call yang lebih jelas. Nilainya baru terasa jika kompleksitas backend memang sudah layak dipisah.
3. Latensi
API Route di Next.js bisa mengurangi hop arsitektur karena UI dan endpoint berada dalam satu aplikasi. Untuk kebutuhan sederhana, ini sering membantu.
BFF terpisah menambah satu hop jaringan. Tetapi dalam sistem nyata, bottleneck sering bukan hop itu sendiri, melainkan panggilan ke upstream yang lambat, serialisasi berlebihan, atau orkestrasi yang tidak efisien. Jika BFF membuat caching, concurrency, dan error handling lebih rapi, latensi total justru bisa lebih stabil.
Prinsip praktis: jangan memutuskan hanya berdasarkan asumsi “service terpisah pasti lebih lambat”. Ukur alur request yang sebenarnya dan lihat di mana waktu habis.
4. Deployment dan release management
Dengan API Route terintegrasi, perubahan UI dan backend pendukung dapat dirilis bersama. Ini nyaman saat kontrak data sering berubah cepat.
Dengan BFF terpisah, frontend dan backend bisa dirilis independen. Ini berguna saat backend butuh lifecycle sendiri, misalnya patch tanpa menyentuh UI, atau saat beberapa tim bekerja paralel dengan jadwal berbeda.
5. Observability dan debugging
Pada sistem kecil, observability dasar di aplikasi Next.js sering cukup. Tetapi ketika endpoint menjadi lapisan integrasi penting, Anda biasanya membutuhkan:
- trace antar dependency,
- metrics per endpoint dan per upstream,
- structured logging yang konsisten,
- korelasi error lintas service.
Semua itu bisa dilakukan di aplikasi terintegrasi, tetapi sering lebih mudah dikelola jika backend dipisahkan sebagai unit operasional yang jelas.
6. Batas runtime dan karakter workload
Tidak semua pekerjaan backend nyaman dijalankan di Route Handler. Masalah biasanya muncul pada workload seperti:
- request yang lama dan melibatkan banyak upstream,
- transformasi payload besar,
- kebutuhan library tertentu yang lebih natural di service backend biasa,
- proses yang seharusnya asynchronous, bukan sinkron dalam request-response web.
Jika endpoint Anda mulai terasa seperti worker, scheduler, atau orchestrator berat, itu sinyal desain perlu dipikirkan ulang. Bukan berarti Next.js tidak bisa, tetapi mungkin bukan tempat terbaik untuk menaruh semuanya.
7. Ownership tim
Ini sering lebih menentukan daripada teknologi. Bila satu tim penuh mengelola pengalaman web end-to-end, pendekatan terintegrasi sangat produktif. Tetapi jika ada tim platform/backend yang harus menjaga kontrak API, reliability, dan integrasi lintas consumer, BFF terpisah memberi batas tanggung jawab yang lebih sehat.
8. Maintainability jangka panjang
Maintainability bukan hanya soal jumlah file. Ia menyangkut apakah orang baru bisa memahami di mana logika bisnis berada, bagaimana perubahan aman dilakukan, dan seberapa mudah dependency di-upgrade tanpa merusak area lain.
Pendekatan terintegrasi cenderung mudah di awal, tetapi berisiko menjadi blob backend di dalam aplikasi frontend jika tidak disiplin. Sementara BFF terpisah lebih mahal di awal, namun sering memberi struktur yang lebih tahan terhadap pertumbuhan domain.
Matriks keputusan: pilih yang mana?
| Kriteria | API Route / Route Handler | BFF Terpisah |
|---|---|---|
| Kompleksitas bisnis | Rendah sampai menengah | Menengah sampai tinggi |
| Jumlah upstream API | Sedikit | Banyak atau heterogen |
| Kecepatan delivery awal | Sangat baik | Lebih lambat di awal |
| Biaya operasional | Lebih rendah | Lebih tinggi |
| Skalabilitas independen | Terbatas | Lebih fleksibel |
| Observability backend | Cukup untuk kasus sederhana | Lebih kuat untuk sistem kompleks |
| Consumer API | Utamanya web app yang sama | Multi-channel atau lintas tim |
| Ownership tim | Tim gabungan kecil | Tim terpisah atau domain jelas |
| Maintainability jangka panjang | Baik jika scope dijaga | Baik jika backend memang domain sendiri |
Checklist evaluasi sebelum memutuskan
- Berapa banyak upstream yang harus diorkestrasi? Jika lebih dari sekadar beberapa panggilan sederhana, pertimbangkan pemisahan.
- Apakah API hanya untuk web ini? Jika akan dipakai consumer lain, BFF/service terpisah lebih masuk akal.
- Apakah tim frontend dan backend punya ownership berbeda? Jika ya, batas service biasanya membantu.
- Apakah endpoint mulai memuat logika domain yang besar? Jika ya, jangan biarkan semua menumpuk di layer Next.js.
- Apakah Anda butuh scaling yang berbeda antara UI dan backend? Jika ya, service terpisah memberi kontrol lebih baik.
- Apakah masalah utama saat ini benar-benar arsitektur? Kadang bottleneck hanya query buruk, caching belum ada, atau integrasi serial yang seharusnya paralel.
- Bisakah kebutuhan itu diselesaikan dengan disiplin struktur internal dulu? Jangan lompat ke service baru kalau masalahnya belum cukup besar.
Risiko over-engineering yang sering terjadi
Banyak tim memisahkan BFF terlalu dini karena ingin “arsitektur rapi” sejak awal. Hasilnya justru:
- velocity turun karena semua perubahan kecil harus lewat dua repo,
- kontrak API berubah terlalu sering karena domain belum stabil,
- tim kecil tersandera overhead deployment dan monitoring,
- bug makin sulit ditelusuri karena kompleksitas bertambah sebelum ada manfaatnya.
Over-engineering juga bisa terjadi di sisi sebaliknya: semua logika terus dipaksa tinggal di Next.js meskipun sudah jelas membutuhkan lifecycle backend sendiri. Akibatnya aplikasi frontend berubah menjadi backend serba guna yang sulit dikelola.
Target yang sehat bukan “semua harus terpisah” atau “semua harus jadi satu”, melainkan memilih batas arsitektur sesuai kompleksitas yang nyata saat ini.
Pola implementasi yang membantu jika tetap memakai Route Handler
Jika Anda memilih pendekatan terintegrasi, hindari menaruh semua logika langsung di file route. Pisahkan lapisan agar nanti mudah diekstrak ke service terpisah bila dibutuhkan.
// app/api/customers/[id]/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { getCustomerProfile } from '@/backend/customers/service'
export async function GET(
_req: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const result = await getCustomerProfile(params.id)
return NextResponse.json(result)
} catch (error) {
return NextResponse.json(
{ message: 'Gagal mengambil data customer' },
{ status: 500 }
)
}
}Lalu letakkan orkestrasi di modul terpisah:
// backend/customers/service.ts
import { crmClient } from '@/backend/integrations/crm'
import { billingClient } from '@/backend/integrations/billing'
export async function getCustomerProfile(customerId: string) {
const [customer, billing] = await Promise.all([
crmClient.getCustomer(customerId),
billingClient.getAccountSummary(customerId),
])
return {
id: customer.id,
name: customer.name,
plan: billing.plan,
balance: billing.balance,
}
}Pola ini penting karena:
- route tetap tipis,
- logika bisnis bisa diuji tanpa bergantung pada layer web,
- migrasi ke BFF terpisah nanti cukup memindahkan modul service dan adapter integrasinya.
Strategi migrasi bertahap ke BFF tanpa rewrite besar
Jika aplikasi Anda sudah berjalan dengan Route Handler dan mulai terasa sesak, Anda tidak perlu rewrite total. Migrasi terbaik biasanya inkremental.
1. Tipiskan Route Handler terlebih dahulu
Pindahkan validasi, transformasi, dan integrasi upstream ke modul internal yang tidak bergantung pada Next.js. Route Handler cukup menjadi adapter HTTP.
2. Kelompokkan per domain, bukan per layar
Hindari struktur yang hanya mengikuti halaman UI. Bentuk modul seperti customers, billing, reporting, atau tenant-settings. Ini mempermudah ekstraksi service per domain jika nanti diperlukan.
3. Identifikasi endpoint yang paling bermasalah
Mulailah dari area dengan ciri berikut:
- sering timeout,
- melibatkan banyak upstream,
- sulit dimonitor,
- sering berubah oleh tim berbeda,
- dibutuhkan consumer lain selain web.
4. Ekstrak satu service tipis lebih dulu
Jangan memecah semuanya sekaligus. Ambil satu domain atau satu kelompok endpoint yang memang paling layak dipisah. Next.js tetap menjadi facade untuk area lain yang belum perlu dipindah.
5. Pertahankan kontrak data yang stabil
Saat memindahkan logika ke BFF, usahakan kontrak respons ke UI tidak banyak berubah. Dengan begitu frontend tidak perlu rewrite besar. Route lama bisa sementara menjadi proxy atau adapter ke service baru.
6. Tambahkan observability sebelum dan sesudah migrasi
Tanpa baseline, Anda tidak akan tahu apakah pemisahan service benar-benar membantu. Catat error rate, durasi request, dan titik upstream yang paling sering gagal sebelum memindahkan area tertentu.
Skenario keputusan nyata
Dashboard internal
Rekomendasi default: pakai Route Handler di Next.js terlebih dahulu.
Alasannya:
- tim biasanya kecil,
- consumer terbatas,
- perubahan UI cepat,
- biaya operasional harus ditekan.
Naikkan level ke BFF hanya jika dashboard berubah menjadi hub integrasi kompleks atau dipakai banyak unit dengan beban domain yang terus tumbuh.
SaaS multi-tenant
Rekomendasi: mulai dari terintegrasi jika scope awal sempit, tetapi siapkan struktur yang mudah diekstrak. Jika tenant logic, entitlement, billing, dan integrasi antar-service makin padat, BFF terpisah biasanya lebih sehat.
Integrasi banyak upstream API
Rekomendasi: condong ke BFF terpisah lebih awal, terutama jika web app harus menjadi permukaan dari banyak sistem backend berbeda. Kompleksitas error handling dan observability biasanya cepat melewati batas nyaman Route Handler sederhana.
Kesalahan umum dan tips debugging
Kesalahan umum
- Menaruh semua logika di file route hingga sulit diuji.
- Menganggap pemisahan service otomatis meningkatkan performa tanpa pengukuran.
- Terlalu cepat membuat BFF padahal consumer masih satu dan domain belum stabil.
- Tidak mendefinisikan ownership sehingga service terpisah justru membingungkan siapa yang bertanggung jawab.
- Mencampur concern presentasi dan domain tanpa batas modul yang jelas.
Tips debugging arsitektur yang mulai tidak sehat
- Lihat endpoint mana yang paling sering berubah bersamaan dengan banyak halaman. Itu sering tanda kontrak backend belum tepat.
- Identifikasi request yang memanggil terlalu banyak upstream secara serial. Kadang masalahnya cukup di paralelisasi atau caching.
- Periksa apakah error dari upstream bisa dipetakan dengan konsisten. Jika tidak, backend orchestration Anda mungkin sudah terlalu kompleks untuk tetap tersebar di layer route.
- Ukur waktu per dependency, bukan hanya total durasi request.
Kesimpulan
Tidak ada jawaban universal untuk Next.js: trade-off BFF terpisah vs API Route di App Router. Jika aplikasi Anda masih fokus pada satu web app, logika backend ringan, tim kecil, dan perubahan cepat, Route Handler terintegrasi biasanya cukup dan paling efisien. Tetapi jika backend mulai menjadi lapisan orkestrasi serius dengan banyak upstream, observability matang, ownership tim terpisah, atau kebutuhan scaling yang berbeda, BFF terpisah memberi batas arsitektur yang lebih tahan lama.
Keputusan terbaik biasanya bukan memilih salah satu secara ideologis, melainkan mulai dari yang paling sederhana yang masih aman, lalu memisahkan hanya ketika kompleksitas nyata sudah menuntutnya. Dengan struktur kode yang disiplin, Anda bisa berangkat dari Route Handler dan bermigrasi ke BFF secara bertahap tanpa rewrite besar.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!