GraphQL adalah pendekatan untuk membangun API yang memberi klien kendali lebih besar atas data yang ingin diambil. Berbeda dengan REST yang biasanya menyediakan banyak endpoint untuk resource berbeda, GraphQL umumnya menyediakan satu endpoint dengan schema yang mendefinisikan tipe data, query, dan mutation yang tersedia.

Dalam konteks Laravel, GraphQL menarik karena Laravel sudah memiliki fondasi yang kuat untuk membangun backend: routing, container, middleware, Eloquent ORM, validasi, autentikasi, dan testing. Dengan menggabungkan GraphQL dan Laravel, kita bisa membangun API yang fleksibel sekaligus tetap terstruktur.

Artikel ini membahas dasar-dasar GraphQL dengan Laravel secara praktis. Fokusnya bukan pada teori semata, tetapi pada bagaimana cara mengimplementasikan GraphQL API, menghubungkannya ke model Eloquent, serta memahami trade-off yang perlu diperhatikan.

Mengapa Memilih GraphQL di Laravel?

Sebelum masuk ke implementasi, penting untuk memahami kapan GraphQL cocok digunakan.

Kelebihan GraphQL

  • Klien bisa meminta field yang benar-benar dibutuhkan, sehingga mengurangi over-fetching.
  • Relasi data lebih mudah diakses dalam satu request, misalnya user beserta post dan komentar.
  • Schema bersifat eksplisit, sehingga kontrak API lebih jelas.
  • Mendukung evolusi API tanpa terlalu bergantung pada versioning endpoint seperti di REST.

Kapan GraphQL Cocok Digunakan?

GraphQL sangat berguna jika frontend membutuhkan fleksibilitas tinggi, misalnya aplikasi SPA, mobile app, atau sistem dengan banyak variasi tampilan data. Jika kebutuhan API Anda sederhana, resource-nya stabil, dan pola akses datanya tidak kompleks, REST bisa jadi lebih mudah dikelola.

Trade-off yang Perlu Dipahami

  • Kompleksitas server lebih tinggi dibanding REST sederhana.
  • Caching HTTP lebih tidak langsung karena banyak operasi lewat satu endpoint.
  • Risiko query berat lebih besar jika schema dan resolver tidak dirancang dengan hati-hati.
  • Debugging SQL dan N+1 query menjadi isu yang cukup umum.

GraphQL bukan pengganti mutlak REST. Ia adalah pilihan arsitektur API yang sangat berguna pada kasus tertentu, terutama ketika kebutuhan data di sisi klien sering berubah.

Konsep Dasar GraphQL

GraphQL bekerja berdasarkan schema. Schema mendefinisikan tipe data apa saja yang tersedia dan operasi apa yang boleh dilakukan klien.

Type

Type mendefinisikan struktur data. Contoh sederhana:

type User {
  id: ID!
  name: String!
  email: String!
}

Tanda ! berarti field tersebut tidak boleh bernilai null.

Query

Query digunakan untuk membaca data:

type Query {
  users: [User!]!
  user(id: ID!): User
}

Contoh request dari klien:

query {
  users {
    id
    name
  }
}

Server hanya mengembalikan field yang diminta.

Mutation

Mutation digunakan untuk mengubah data:

type Mutation {
  createUser(name: String!, email: String!, password: String!): User!
}

Resolver

Resolver adalah fungsi yang bertanggung jawab mengambil atau memproses data untuk field tertentu. Dalam Laravel, resolver biasanya berinteraksi langsung dengan model Eloquent, service class, atau repository.

Menyiapkan GraphQL di Laravel

Laravel tidak menyediakan GraphQL server secara bawaan, jadi kita biasanya menggunakan package komunitas. Salah satu yang populer adalah rebing/graphql-laravel. Package ini cukup umum dipakai untuk integrasi GraphQL dengan Laravel dan memiliki pola yang familiar bagi developer Laravel.

Instalasi Package

composer require rebing/graphql-laravel

Jika diperlukan, publish file konfigurasi:

php artisan vendor:publish --provider="Rebing\GraphQL\GraphQLServiceProvider"

Setelah itu, Anda biasanya akan mendapatkan file konfigurasi seperti config/graphql.php.

Membuat Model dan Migrasi

Misalkan kita ingin membuat API GraphQL untuk entitas User dan Post. Untuk contoh sederhana, kita fokus ke Post.

php artisan make:model Post -m

Contoh isi migrasi:

Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->cascadeOnDelete();
    $table->string('title');
    $table->text('body');
    $table->timestamps();
});

Lalu jalankan:

php artisan migrate

Relasi Eloquent

Di model Post:

class Post extends Model
{
    protected $fillable = ['user_id', 'title', 'body'];

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

Di model User:

class User extends Authenticatable
{
    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}

Mendefinisikan Type, Query, dan Mutation

Dengan package GraphQL di Laravel, umumnya kita membuat class untuk type, query, dan mutation.

Membuat GraphQL Type

Contoh type untuk Post:

namespace App\GraphQL\Types;

use App\Models\Post;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Type as GraphQLType;

class PostType extends GraphQLType
{
    protected $attributes = [
        'name' => 'Post',
        'model' => Post::class,
    ];

    public function fields(): array
    {
        return [
            'id' => [
                'type' => Type::nonNull(Type::id()),
            ],
            'title' => [
                'type' => Type::nonNull(Type::string()),
            ],
            'body' => [
                'type' => Type::nonNull(Type::string()),
            ],
            'created_at' => [
                'type' => Type::string(),
            ],
        ];
    }
}

Type ini mendefinisikan field yang bisa diminta klien.

Membuat Query

Contoh query untuk mengambil semua post:

namespace App\GraphQL\Queries;

use App\Models\Post;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Query;

class PostsQuery extends Query
{
    protected $attributes = [
        'name' => 'posts',
    ];

    public function type(): Type
    {
        return Type::listOf(GraphQL::type('Post'));
    }

    public function resolve($root, $args)
    {
        return Post::query()->latest()->get();
    }
}

Contoh query untuk mengambil satu post berdasarkan ID:

class PostQuery extends Query
{
    protected $attributes = [
        'name' => 'post',
    ];

    public function type(): Type
    {
        return GraphQL::type('Post');
    }

    public function args(): array
    {
        return [
            'id' => ['type' => Type::nonNull(Type::id())],
        ];
    }

    public function resolve($root, $args)
    {
        return Post::find($args['id']);
    }
}

Membuat Mutation

Contoh mutation untuk membuat post baru:

namespace App\GraphQL\Mutations;

use App\Models\Post;
use GraphQL\Type\Definition\Type;
use Illuminate\Support\Facades\Auth;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Mutation;

class CreatePostMutation extends Mutation
{
    protected $attributes = [
        'name' => 'createPost',
    ];

    public function type(): Type
    {
        return GraphQL::type('Post');
    }

    public function args(): array
    {
        return [
            'title' => ['type' => Type::nonNull(Type::string())],
            'body' => ['type' => Type::nonNull(Type::string())],
        ];
    }

    public function resolve($root, $args)
    {
        return Post::create([
            'user_id' => Auth::id(),
            'title' => $args['title'],
            'body' => $args['body'],
        ]);
    }
}

Di dunia nyata, mutation seperti ini sebaiknya dilindungi autentikasi dan validasi yang lebih ketat.

Mendaftarkan Schema

Pada file konfigurasi GraphQL, Anda perlu mendaftarkan type, query, dan mutation yang dibuat. Struktur detailnya bergantung pada package, tetapi idenya adalah menghubungkan nama GraphQL dengan class Laravel yang sesuai.

Contoh Query dan Mutation dari Klien

Setelah server siap, klien bisa mengirim request GraphQL ke endpoint yang disediakan, misalnya /graphql.

Mengambil Data

query {
  posts {
    id
    title
    body
  }
}

Membuat Data

mutation {
  createPost(title: "Belajar GraphQL", body: "Isi artikel") {
    id
    title
    body
  }
}

Salah satu kelebihan GraphQL terlihat di sini: setelah mutation dijalankan, klien bisa langsung memilih field apa saja yang ingin dikembalikan.

Validasi, Autentikasi, dan Otorisasi

GraphQL tetaplah API backend. Jadi, prinsip keamanan di Laravel tetap berlaku.

Validasi Input

Jangan mengandalkan schema GraphQL saja. Tipe seperti String! hanya memastikan nilai wajib ada dan tidak null, tetapi tidak memvalidasi panjang, format, atau aturan bisnis.

Untuk itu, tambahkan validasi Laravel di resolver atau gunakan pola service layer:

validator($args, [
    'title' => ['required', 'string', 'max:255'],
    'body' => ['required', 'string'],
])->validate();

Autentikasi

Jika mutation membutuhkan user login, pastikan endpoint GraphQL menggunakan middleware yang sesuai atau lakukan pengecekan di resolver. Untuk API token, biasanya integrasi dilakukan dengan Sanctum atau solusi autentikasi lain yang digunakan proyek Anda.

Otorisasi

Gunakan policy Laravel untuk memastikan user hanya bisa membaca atau mengubah resource yang memang menjadi haknya. Misalnya, hanya penulis post yang boleh mengedit post tersebut.

Kesalahan umum adalah menganggap schema GraphQL sudah cukup aman. Padahal, validasi, autentikasi, dan otorisasi tetap harus diterapkan seperti pada endpoint REST.

Masalah Umum: N+1 Query dan Performa

Karena GraphQL memungkinkan klien meminta nested relation, masalah N+1 query sangat sering muncul. Misalnya, query meminta daftar post beserta user-nya. Jika resolver tidak menggunakan eager loading, Laravel bisa mengeksekusi satu query untuk posts lalu satu query tambahan untuk setiap post.

Contoh yang Perlu Dihindari

return Post::all();

Jika klien juga meminta field user, ini bisa memicu query tambahan berulang.

Pendekatan yang Lebih Baik

return Post::with('user')->latest()->get();

Untuk schema yang lebih kompleks, Anda mungkin perlu menerapkan batching atau data loader pattern, tergantung package dan arsitektur aplikasi.

Batasi Query yang Terlalu Berat

GraphQL memberi fleksibilitas tinggi, tetapi itu juga berarti klien bisa mengirim query yang sangat dalam atau terlalu luas. Beberapa langkah mitigasi:

  • Batasi pagination untuk collection besar.
  • Gunakan eager loading secara selektif.
  • Terapkan pembatasan depth atau complexity jika package mendukung.
  • Monitor query SQL yang dihasilkan.

Testing dan Debugging

Testing Query dan Mutation

Di Laravel, Anda bisa menguji endpoint GraphQL dengan HTTP test biasa. Contoh sederhana:

public function test_can_fetch_posts()
{
    $response = $this->postJson('/graphql', [
        'query' => '{ posts { id title } }'
    ]);

    $response->assertOk()
             ->assertJsonStructure([
                 'data' => [
                     'posts' => [
                         ['id', 'title']
                     ]
                 ]
             ]);
}

Testing seperti ini penting karena GraphQL sering menyatukan banyak operasi di satu endpoint. Tanpa test, perubahan schema atau resolver bisa memengaruhi banyak klien sekaligus.

Debugging Error GraphQL

Beberapa tips saat debugging:

  • Periksa bagian errors pada response GraphQL.
  • Lihat log Laravel untuk exception internal.
  • Aktifkan query log database saat melacak performa.
  • Pastikan nama type, query, dan mutation yang didaftarkan cocok dengan yang dipanggil klien.
  • Jika field tidak muncul, cek definisi schema dan field selection di request.

Praktik Terbaik dalam Proyek Nyata

Jangan Penuhi Resolver dengan Logika Bisnis

Resolver sebaiknya tipis. Letakkan logika bisnis di service class, action class, atau domain layer agar lebih mudah diuji dan dirawat.

Gunakan Pagination

Untuk data yang besar, jangan kembalikan seluruh record sekaligus. Sediakan argumen seperti limit, page, atau pendekatan cursor jika dibutuhkan.

Rancang Schema Secara Stabil

Nama field dan type adalah kontrak publik. Hindari perubahan sembarangan. Jika perlu menghapus field, lakukan deprecation terlebih dahulu agar klien punya waktu bermigrasi.

Selaraskan dengan Eloquent, tapi Jangan Terkunci

Menghubungkan GraphQL type langsung ke model Eloquent itu praktis, tetapi jangan sampai schema hanya menjadi cerminan mentah dari tabel database. Schema sebaiknya merepresentasikan kebutuhan domain dan konsumsi klien, bukan struktur database semata.

Penutup

Membangun GraphQL API dengan Laravel pada dasarnya melibatkan tiga hal utama: mendefinisikan schema, menulis resolver yang mengambil atau memodifikasi data, lalu menjaga performa dan keamanan seperti API backend pada umumnya.

Laravel membuat banyak bagian implementasi menjadi lebih nyaman, terutama saat terhubung dengan Eloquent, validasi, middleware, dan testing. Namun, fleksibilitas GraphQL juga menuntut disiplin desain yang lebih baik. Tanpa schema yang rapi, eager loading yang tepat, dan kontrol akses yang jelas, API bisa cepat menjadi sulit dirawat.

Jika Anda baru memulai, bangun dulu schema yang kecil: satu atau dua type, beberapa query, dan satu mutation. Setelah itu, evaluasi pola akses data, pantau query database, lalu kembangkan secara bertahap. Pendekatan ini biasanya lebih aman dan lebih mudah dipelajari daripada langsung membangun schema besar sejak awal.