Web Development

Tutorial RESTful API Skalabel dengan Laravel & PostgreSQL

R

Raka Haikal

Tim Teknis Arkatek

·
7 min baca
·
13x dibaca
Ilustrasi Tutorial RESTful API Skalabel dengan Laravel & PostgreSQL

Membangun antarmuka pemrograman aplikasi (REST API) yang siap dipakai di produksi bukan sekadar menulis rute dasar dan controller CRUD. Di lingkungan enterprise, backend dituntut menjaga integritas data melalui transaksi atomik, menghindari query berulang (N+1 problem), dan tetap responsif saat tabel tumbuh sampai jutaan baris tanpa membebani CPU server basis data.

Kombinasi framework Laravel (PHP modern) dan sistem basis data PostgreSQL adalah salah satu stack yang paling teruji untuk kebutuhan ini. Tutorial ini membedah arsitekturnya langkah demi langkah: prasyarat lingkungan, perintah Artisan, pemisahan Controller-Service, validasi Form Request, standarisasi API Resource, pengindeksan yang dibuktikan dengan EXPLAIN, cursor pagination, rate limiting, hingga pengujian otomatis.

Poin Penting / Key Takeaways
  • Pisahkan tanggung jawab dengan pola Controller-Service: controller tipis, logika bisnis dan transaksi di Service Layer. Repository baru diperlukan bila query kompleks dipakai ulang di banyak tempat.
  • Bungkus operasi tulis multi-tabel di DB::transaction() dan kunci baris yang akan diubah dengan lockForUpdate() secara terurut untuk menjamin ACID tanpa deadlock.
  • Rancang indeks berdasarkan query yang benar-benar dijalankan, lalu buktikan penggunaannya dengan EXPLAIN (ANALYZE, BUFFERS).
  • Gunakan cursorPaginate() untuk daftar besar agar biaya query tidak membengkak di halaman yang dalam, dengan konsekuensi tidak ada total halaman.
  • Standarkan keluaran dengan API Resource dan whenLoaded() agar struktur JSON konsisten, N+1 terhindar, dan kolom privat tidak bocor.
  • Lindungi endpoint dengan Sanctum, rate limiting bertingkat, dan tes otomatis untuk skenario kritis seperti stok tidak cukup.

Prasyarat & Lingkungan Pengembangan (Prerequisites)

Sebelum memulai, pastikan lingkungan pengembangan lokal atau server Anda memenuhi spesifikasi berikut:

  • Framework: Laravel 13 (rilis Maret 2026) direkomendasikan untuk proyek baru. Laravel 12 masih menerima perbaikan keamanan hingga Februari 2027, tetapi perbaikan bug reguler sudah berakhir pada Agustus 2026.

  • PHP: Versi 8.3 atau lebih baru untuk Laravel 13 (8.2 untuk Laravel 12), dengan ekstensi aktif: pdo_pgsql, mbstring, bcmath, openssl, dan tokenizer.

  • Database: PostgreSQL 16 atau lebih baru (bisa lokal atau lewat container Docker).

  • Package Manager: Composer versi 2.2+.

  • API Testing Client: Postman, Insomnia, atau ekstensi VS Code Thunder Client untuk menguji endpoint HTTP.

  • Pengetahuan Dasar: Object-Oriented Programming (OOP) PHP, dependency injection, dan konsep REST API (HTTP methods dan status codes).

Pemeriksaan Ekstensi pdo_pgsql
Pastikan ekstensi pdo_pgsql aktif di file php.ini Anda. Verifikasi lewat terminal dengan perintah: php -m | grep pdo_pgsql. Jika belum aktif, aktifkan baris extension=pdo_pgsql di php.ini.

Daftar Perintah CLI & Artisan Generator

Laravel menyediakan perintah bawaan (Artisan Console) untuk men-generate class dengan namespace dan struktur standar. Perhatikan langkah install:api: sejak Laravel 11, file routes/api.php tidak ada secara default dan perintah ini membuatnya sekaligus memasang Sanctum.

Terminal / CLI
BASH
1# 1. Buat proyek dan aktifkan API routing (membuat routes/api.php dan memasang Sanctum)
2composer create-project laravel/laravel:^13.0 arkatek-orders-api
3cd arkatek-orders-api
4php artisan install:api
5
6# 2. Konfigurasi koneksi PostgreSQL di file .env
7# DB_CONNECTION=pgsql
8# DB_HOST=127.0.0.1
9# DB_PORT=5432
10# DB_DATABASE=arkatek_production
11# DB_USERNAME=postgres
12# DB_PASSWORD=secret
13
14# 3. Model, migration, dan factory
15php artisan make:model Customer -mf
16php artisan make:model Product -mf
17php artisan make:model Order -m
18php artisan make:model OrderItem -m
19
20# 4. Form Request untuk validasi payload API
21php artisan make:request StoreOrderRequest
22
23# 5. Service Layer dan exception domain
24mkdir -p app/Services
25touch app/Services/OrderService.php
26php artisan make:exception InsufficientStockException
27
28# 6. API Resource untuk kontrak JSON
29php artisan make:resource OrderResource
30
31# 7. API Controller (hanya rute index dan store yang dipakai)
32php artisan make:controller Api/OrderController --api
33
34# 8. Migrasi tabel dan indeks ke PostgreSQL, lalu jalankan server
35php artisan migrate
36php artisan serve
Perintah Artisan untuk men-generate komponen arsitektur API yang terisolasi.

Migrasi customers, products, dan order_items mengikuti pola yang sama dengan kolom minimal berikut:

  • customers: name, email.

  • products: name, price (decimal(12,2)), stock (unsignedInteger).

  • order_items: order_id (cascade on delete), product_id (restrict on delete), quantity, unit_price, subtotal.

Model Order membutuhkan $fillable dan relasi. Model OrderItem membutuhkan $fillable untuk product_id, quantity, unit_price, subtotal serta relasi product(), karena createMany() memakai mass assignment.

app/Models/Order.php
PHP
1namespace App\Models;
2
3use Illuminate\Database\Eloquent\Model;
4use Illuminate\Database\Eloquent\Relations\BelongsTo;
5use Illuminate\Database\Eloquent\Relations\HasMany;
6
7class Order extends Model
8{
9 protected $fillable = ["user_id", "customer_id", "order_number", "total_amount", "status", "payment_method"];
10
11 public function customer(): BelongsTo
12 {
13 return $this->belongsTo(Customer::class);
14 }
15
16 public function items(): HasMany
17 {
18 return $this->hasMany(OrderItem::class);
19 }
20}
Model Order dengan mass assignment eksplisit dan relasi yang akan di-eager load.

Terakhir, definisikan rute versi 1 di routes/api.php. Middleware auth:sanctum dijalankan lebih dulu agar limiter di bagian rate limiting dapat mengenali pengguna:

routes/api.php
PHP
1use App\Http\Controllers\Api\OrderController;
2use Illuminate\Support\Facades\Route;
3
4Route::prefix("v1")
5 ->middleware(["auth:sanctum", "throttle:orders-api"])
6 ->group(function () {
7 Route::apiResource("orders", OrderController::class)->only(["index", "store"]);
8 });
Rute API versi 1 yang dilindungi autentikasi dan rate limiting.

1. Skema Migrasi & Pengindeksan PostgreSQL

Performa database pada jutaan baris data dimulai dari indeks yang dirancang berdasarkan query yang benar-benar dijalankan. Tabel orders di bawah melayani dua pola akses: daftar pesanan berdasarkan status (endpoint listing) dan filter per pelanggan.

database/migrations/create_orders_table.php
PHP
1use Illuminate\Database\Migrations\Migration;
2use Illuminate\Database\Schema\Blueprint;
3use Illuminate\Support\Facades\Schema;
4
5return new class extends Migration
6{
7 public function up(): void
8 {
9 Schema::create("orders", function (Blueprint $table) {
10 $table->id();
11 $table->foreignId("user_id")->constrained()->restrictOnDelete();
12 $table->foreignId("customer_id")->constrained()->restrictOnDelete();
13 $table->string("order_number")->unique();
14 $table->string("status"); // PENDING, PROCESSING, COMPLETED, CANCELLED
15 $table->string("payment_method");
16 $table->decimal("total_amount", 12, 2);
17 $table->timestamps();
18
19 // Endpoint listing: WHERE status = ? ORDER BY id DESC
20 $table->index(["status", "id"]);
21
22 // Filter per pelanggan: WHERE customer_id = ? AND status = ? ORDER BY created_at
23 $table->index(["customer_id", "status", "created_at"]);
24 });
25 }
26
27 public function down(): void
28 {
29 Schema::dropIfExists("orders");
30 }
31};
Migrasi orders dengan dua indeks komposit yang masing-masing melayani satu pola query.

Ada dua hal penting di sini. Pertama, indeks komposit hanya efektif bila query memfilter kolom paling kiri (leftmost prefix). Indeks (customer_id, status, created_at) tidak membantu query WHERE status = ... ORDER BY id DESC karena customer_id tidak difilter, itu sebabnya endpoint listing memakai indeks (status, id) tersendiri. Kedua, PostgreSQL tidak membuat indeks otomatis untuk kolom foreign key, sehingga indeks komposit berawalan customer_id juga melayani pencarian per pelanggan. Untuk data transaksi, gunakan restrictOnDelete() agar riwayat pesanan tidak ikut terhapus saat user atau pelanggan dihapus.

Buktikan dengan EXPLAIN (ANALYZE, BUFFERS)

Jangan menebak apakah indeks dipakai. Jalankan EXPLAIN pada database uji berisi beberapa juta baris, lalu bandingkan sebelum dan sesudah indeks dibuat. Cari Index Scan (bukan Seq Scan) serta perhatikan Execution Time dan jumlah buffer yang dibaca. Query dibatasi 21 baris karena cursorPaginate(20) mengambil satu baris ekstra untuk mengetahui apakah masih ada halaman berikutnya.

psql
SQL
1EXPLAIN (ANALYZE, BUFFERS)
2SELECT * FROM orders
3WHERE status = 'PENDING'
4ORDER BY id DESC
5LIMIT 21;
Verifikasi penggunaan indeks (status, id) pada query listing.

Untuk teknik diagnosis query lambat yang lebih luas, baca Strategi Optimasi Database Skala Besar.

2. Implementasi Lapisan Validasi: Form Request

Validasi ketat adalah benteng pertahanan pertama API. Buat Form Request khusus untuk memvalidasi pembuatan pesanan, termasuk batas jumlah item agar payload tidak bisa dibuat sangat besar:

app/Http/Requests/StoreOrderRequest.php
PHP
1namespace App\Http\Requests;
2
3use Illuminate\Foundation\Http\FormRequest;
4use Illuminate\Validation\Rule;
5
6class StoreOrderRequest extends FormRequest
7{
8 public function authorize(): bool
9 {
10 return true; // otorisasi rute ditangani middleware auth:sanctum
11 }
12
13 public function rules(): array
14 {
15 return [
16 "customer_id" => ["required", "integer", "exists:customers,id"],
17 "items" => ["required", "array", "min:1", "max:50"],
18 "items.*.product_id" => ["required", "integer", "exists:products,id"],
19 "items.*.quantity" => ["required", "integer", "min:1", "max:100"],
20 "payment_method" => ["required", "string", Rule::in(["BANK_TRANSFER", "E_WALLET", "CREDIT_CARD"])],
21 ];
22 }
23}
Validasi payload pesanan multi-item menggunakan Form Request yang terisolasi.

3. Implementasi Service Layer: Transaksi Atomik & Penguncian Baris

Seluruh logika bisnis yang memotong stok dan menghitung harga wajib dibungkus dalam transaksi atomik (DB::transaction) dengan penguncian baris (lockForUpdate). Dua detail penting: semua produk dikunci sekaligus dalam satu query yang diurutkan berdasarkan id (menghindari deadlock dan query per item), dan perhitungan uang memakai bcmath agar tidak kehilangan presisi.

app/Services/OrderService.php
PHP
1namespace App\Services;
2
3use App\Exceptions\InsufficientStockException;
4use App\Models\Order;
5use App\Models\Product;
6use Illuminate\Support\Facades\DB;
7use Illuminate\Support\Str;
8
9class OrderService
10{
11 public function createOrder(int $userId, array $data): Order
12 {
13 return DB::transaction(function () use ($userId, $data) {
14 // Gabungkan item yang product_id-nya sama
15 $quantities = collect($data["items"])
16 ->groupBy("product_id")
17 ->map(fn ($rows) => $rows->sum("quantity"));
18
19 // Kunci semua produk sekaligus, terurut berdasarkan id agar tidak deadlock
20 $products = Product::whereIn("id", $quantities->keys())
21 ->orderBy("id")
22 ->lockForUpdate()
23 ->get()
24 ->keyBy("id");
25
26 $totalAmount = "0.00";
27 $orderItems = [];
28
29 foreach ($quantities as $productId => $quantity) {
30 $product = $products[$productId];
31
32 if ($product->stock < $quantity) {
33 throw new InsufficientStockException($product->name);
34 }
35
36 $product->decrement("stock", $quantity);
37
38 $subtotal = bcmul($product->price, (string) $quantity, 2);
39 $totalAmount = bcadd($totalAmount, $subtotal, 2);
40
41 $orderItems[] = [
42 "product_id" => $product->id,
43 "quantity" => $quantity,
44 "unit_price" => $product->price,
45 "subtotal" => $subtotal,
46 ];
47 }
48
49 $order = Order::create([
50 "user_id" => $userId,
51 "customer_id" => $data["customer_id"],
52 "order_number" => "ORD-" . Str::ulid(),
53 "total_amount" => $totalAmount,
54 "status" => "PENDING",
55 "payment_method" => $data["payment_method"],
56 ]);
57
58 $order->items()->createMany($orderItems);
59
60 // Eager loading relasi untuk respons, tanpa query tambahan per item
61 return $order->load(["customer", "items.product"]);
62 });
63 }
64}
Service Layer dengan transaksi atomik, penguncian baris terurut, dan perhitungan uang presisi.

Kekurangan stok dilempar sebagai exception domain yang merender dirinya sendiri menjadi respons 422, bukan error 500 generik. Transaksi otomatis di-rollback sehingga stok tidak berubah.

app/Exceptions/InsufficientStockException.php
PHP
1namespace App\Exceptions;
2
3use Exception;
4use Illuminate\Http\JsonResponse;
5
6class InsufficientStockException extends Exception
7{
8 public function __construct(public readonly string $productName)
9 {
10 parent::__construct("Stok produk {$productName} tidak mencukupi.");
11 }
12
13 public function render(): JsonResponse
14 {
15 return response()->json(["message" => $this->getMessage()], 422);
16 }
17}
Exception domain yang dirender sebagai respons 422.

4. Optimasi Database: Cursor Pagination

Ketika tabel transaksi mencapai jutaan baris, pagination berbasis offset (LIMIT 20 OFFSET 500000) memaksa database memindai lalu membuang ratusan ribu baris sebelum mengembalikan 20 baris terakhir. Biayanya naik seiring halaman yang makin dalam, dan ini penyebab umum lonjakan CPU pada server database.

CURSOR PAGINATION

Biaya Query per Halaman

Dengan kolom urut yang terindeks, halaman pertama dan halaman ke-100.000 diproses dengan biaya yang hampir sama.

Konstan

Berikut Controller ramping yang menyajikan data dengan cursorPaginate() dan mengeksekusi eager loading di sisi query:

app/Http/Controllers/Api/OrderController.php
PHP
1namespace App\Http\Controllers\Api;
2
3use App\Http\Controllers\Controller;
4use App\Http\Requests\StoreOrderRequest;
5use App\Http\Resources\OrderResource;
6use App\Models\Order;
7use App\Services\OrderService;
8use Illuminate\Http\JsonResponse;
9use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
10
11class OrderController extends Controller
12{
13 public function __construct(private readonly OrderService $orderService)
14 {
15 }
16
17 // GET /api/v1/orders
18 public function index(): AnonymousResourceCollection
19 {
20 $orders = Order::with(["customer", "items.product"])
21 ->where("status", "PENDING")
22 ->orderByDesc("id")
23 ->cursorPaginate(20);
24
25 return OrderResource::collection($orders);
26 }
27
28 // POST /api/v1/orders
29 public function store(StoreOrderRequest $request): JsonResponse
30 {
31 $order = $this->orderService->createOrder(
32 $request->user()->id,
33 $request->validated()
34 );
35
36 return (new OrderResource($order))->response()->setStatusCode(201);
37 }
38}
Controller ramping: mendelegasikan logika ke OrderService dan memakai cursorPaginate.

Kedua endpoint kini memakai envelope bawaan Laravel (data, plus links dan meta pada koleksi), sehingga struktur respons konsisten bagi tim frontend dan mobile.

Cursor Pagination vs Offset Pagination

Keunggulan Cursor Pagination
  • Biaya query tidak bertambah seiring dalamnya halaman, selama kolom urut terindeks.
  • Hasil stabil saat data baru masuk: tidak ada baris duplikat atau terlewat antar halaman.
  • Cocok untuk infinite scroll dan aplikasi mobile.
Batasan yang Perlu Diketahui
  • Tidak ada total data atau total halaman, dan tidak bisa melompat langsung ke halaman tertentu.
  • Kolom pengurut harus terindeks dan unik, atau ditambah pemecah seri seperti id.
  • Cursor bergantung pada urutan yang dipakai, sehingga mengganti urutan membuat cursor lama tidak berlaku.

5. Menstandarisasi Respon dengan Eloquent API Resource

Bungkus data model dengan API Resource untuk menjamin kontrak JSON yang stabil bagi tim frontend maupun mobile, serta mencegah kebocoran kolom rahasia database. Dua penyesuaian penting: relasi dibungkus whenLoaded() agar tidak memicu lazy loading (N+1) bila lupa eager load, dan nilai uang dikembalikan sebagai string desimal, bukan float, agar presisinya terjaga.

app/Http/Resources/OrderResource.php
PHP
1namespace App\Http\Resources;
2
3use Illuminate\Http\Request;
4use Illuminate\Http\Resources\Json\JsonResource;
5
6class OrderResource extends JsonResource
7{
8 public function toArray(Request $request): array
9 {
10 return [
11 "id" => $this->id,
12 "order_number" => $this->order_number,
13 "status" => $this->status,
14 "total_amount" => $this->total_amount, // string desimal, mis. "150000.00"
15 "payment_method" => $this->payment_method,
16 "created_at" => $this->created_at?->toIso8601String(),
17 "customer" => $this->whenLoaded("customer", fn () => [
18 "id" => $this->customer->id,
19 "name" => $this->customer->name,
20 "email" => $this->customer->email,
21 ]),
22 "items" => $this->whenLoaded("items", fn () => $this->items->map(fn ($item) => [
23 "product_id" => $item->product_id,
24 "product_name" => $item->relationLoaded("product") ? $item->product?->name : null,
25 "quantity" => $item->quantity,
26 "unit_price" => $item->unit_price,
27 "subtotal" => $item->subtotal,
28 ])),
29 ];
30 }
31}
API Resource dengan whenLoaded untuk mencegah N+1 dan nilai uang sebagai string desimal.
Deteksi N+1 Sejak Development
Aktifkan Model::preventLazyLoading(! app()->isProduction()) di AppServiceProvider::boot() agar Laravel melempar exception setiap kali ada lazy loading di lingkungan non-produksi. Jika memakai Laravel 13, tersedia juga JSON:API Resources first-party sebagai alternatif bila kontrak API Anda harus mengikuti spesifikasi JSON:API.

6. Keamanan & Stabilitas: Dynamic Rate Limiting

Setiap endpoint harus dilindungi rate limiting untuk mencegah brute-force, scraping agresif, atau kehabisan koneksi basis data. Definisikan limiter bertingkat di AppServiceProvider:

app/Providers/AppServiceProvider.php
PHP
1use Illuminate\Cache\RateLimiting\Limit;
2use Illuminate\Http\Request;
3use Illuminate\Support\Facades\RateLimiter;
4
5public function boot(): void
6{
7 RateLimiter::for("orders-api", function (Request $request) {
8 return $request->user()
9 ? Limit::perMinute(300)->by($request->user()->id)
10 : Limit::perMinute(60)->by($request->ip());
11 });
12}
Rate limiter bertingkat: 300 request per menit untuk pengguna terautentikasi, 60 untuk publik.
  • Tiered Limiting: 300 request/menit per pengguna terautentikasi dan 60 request/menit per IP untuk pengguna publik. Karena middleware autentikasi dijalankan lebih dulu, $request->user() sudah terisi saat limiter dievaluasi.

  • Autentikasi: Sanctum menerbitkan token opaque (bukan JWT) dan cocok untuk API internal atau aplikasi mobile milik sendiri. Untuk skenario OAuth2 atau JWT, ikuti panduan Implementasi Autentikasi Modern (OAuth2 & JWT) pada Aplikasi Web.

  • Fondasi Infrastruktur: Jalankan backend Laravel di lingkungan yang aman sesuai Tutorial Setup Docker Compose Produksi.

7. Menguji API: Buktikan Transaksi Benar-Benar Rollback

Tes otomatis untuk skenario kritis lebih berharga daripada puluhan tes untuk jalur mulus. Tes berikut memastikan pesanan dengan stok tidak cukup ditolak dengan 422 dan stok produk tidak berubah. Isi definisi minimal di factory Customer dan Product terlebih dahulu, lalu jalankan php artisan test.

tests/Feature/CreateOrderTest.php
PHP
1namespace Tests\Feature;
2
3use App\Models\Customer;
4use App\Models\Product;
5use App\Models\User;
6use Illuminate\Foundation\Testing\RefreshDatabase;
7use Laravel\Sanctum\Sanctum;
8use Tests\TestCase;
9
10class CreateOrderTest extends TestCase
11{
12 use RefreshDatabase;
13
14 public function test_stok_tidak_cukup_menghasilkan_422_dan_stok_tidak_berubah(): void
15 {
16 Sanctum::actingAs(User::factory()->create());
17 $customer = Customer::factory()->create();
18 $product = Product::factory()->create(["stock" => 2]);
19
20 $response = $this->postJson("/api/v1/orders", [
21 "customer_id" => $customer->id,
22 "items" => [["product_id" => $product->id, "quantity" => 5]],
23 "payment_method" => "BANK_TRANSFER",
24 ]);
25
26 $response->assertStatus(422);
27 $this->assertSame(2, $product->fresh()->stock);
28 }
29}
Feature test: stok tidak cukup harus ditolak dan tidak mengubah data.
SOLUSI TEKNOLOGI ARKATEK

Butuh Pengembangan Backend API Skalabel dan Berkinerja Tinggi?

Konsultasikan rancangan arsitektur sistem, audit performa database, dan pengembangan software kustom bersama engineer Arkatek.

Pertanyaan Umum Seputar REST API Laravel dan PostgreSQL

Apakah Laravel memerlukan Repository pattern?

Untuk CRUD dan query sederhana, Eloquent sudah cukup. Repository berguna bila Anda ingin mengabstraksi sumber data atau query kompleks dipakai berulang di banyak tempat. Tutorial ini memakai Controller dan Service agar tetap ringkas.

Apa beda cursorPaginate() dan paginate()?

paginate() menyediakan total data dan nomor halaman, tetapi memakai OFFSET dan COUNT yang makin berat pada tabel besar. cursorPaginate() memakai kondisi berbasis kolom terakhir sehingga biayanya stabil, tetapi tanpa total halaman dan lompat halaman.

Bagaimana cara mencegah overselling stok?

Gunakan DB::transaction dengan lockForUpdate() seperti pada tutorial ini. Alternatifnya adalah pembaruan atomik UPDATE products SET stock = stock - ? WHERE id = ? AND stock >= ? lalu memeriksa jumlah baris yang terpengaruh.

Apakah perlu versioning API seperti /api/v1?

Ya, terutama bila API dikonsumsi aplikasi mobile atau pihak ketiga, karena klien lama tidak bisa dipaksa langsung memperbarui aplikasi. Prefix versi memberi ruang mengubah kontrak tanpa merusak klien yang ada.

Kapan memilih PostgreSQL untuk backend Laravel?

PostgreSQL dikenal kuat untuk beban transaksional yang kompleks, dengan tipe data dan opsi pengindeksan yang kaya seperti partial index dan JSONB. MySQL atau MariaDB juga didukung Laravel, jadi pilihlah sesuai kebutuhan proyek dan keahlian tim.

Kesimpulan

Membangun REST API yang siap di tingkat enterprise membutuhkan struktur kode modular, isolasi logika bisnis yang bersih, dan pemahaman tentang cara mesin basis data bekerja. Dengan Service Layer, transaksi atomik dengan penguncian terurut, indeks yang dibuktikan lewat EXPLAIN, cursor pagination, dan tes untuk skenario kritis, backend Anda punya fondasi yang stabil untuk tumbuh. Jika Anda masih menimbang bentuk arsitektur keseluruhan, baca juga Panduan Arsitektur Web Modern: Monolith vs Microservices.

Referensi resmi: Cursor Pagination dan Eloquent API Resources di dokumentasi Laravel, serta Using EXPLAIN di dokumentasi PostgreSQL.

R

// DITULIS OLEH

Raka Haikal

Pengembang perangkat lunak & spesialis arsitektur sistem digital di Arkatek. Berfokus pada pembangunan solusi software scalable, otomatisasi sistem bisnis, dan optimasi performa.

// EKSPLORASI TOPIK

Artikel Terkait Lainnya

Lihat Semua Artikel →