GraphQL adalah bahasa query untuk API yang awalnya dikembangkan oleh Facebook. Berbeda dengan REST API yang mengembalikan data sesuai endpoint yang sudah ditentukan, GraphQL memungkinkan klien meminta persis data yang dibutuhkan melalui satu endpoint saja. Hasilnya: tidak ada lagi masalah over-fetching (menerima data berlebihan) maupun under-fetching (kekurangan data). Di tutorial ini kita akan membangun API GraphQL lengkap dengan Python menggunakan Strawberry dan FastAPI.
Apa Itu GraphQL dan Kenapa Menggunakannya?
Pada REST API, setiap sumber daya biasanya memiliki endpoint sendiri-sendiri (misalnya /users, /users/1/posts). Ketika sebuah halaman butuh data dari banyak sumber, klien harus melakukan banyak request HTTP. GraphQL menyelesaikan ini dengan satu endpoint /graphql dan sebuah schema yang mendeskripsikan semua tipe data dan operasi yang tersedia.
Klien lalu mengirim query untuk menentukan field apa saja yang diinginkan. Server hanya mengembalikan field yang diminta — tidak lebih, tidak kurang. Pendekatan ini sangat cocok untuk aplikasi mobile dan frontend modern yang ingin menghemat bandwidth dan mempercepat waktu loading.
Prasyarat
Sebelum mulai, pastikan kamu sudah memiliki Python 3.10 atau lebih baru dan terbiasa dengan dasar FastAPI. Kamu tidak perlu pengalaman GraphQL sama sekali — kita akan mempelajarinya dari nol.
Menyiapkan Proyek
Buat direktori proyek, siapkan virtual environment, lalu install strawberry-graphql (dengan ekstra FastAPI) dan uvicorn sebagai server ASGI.
mkdir graphql-books
cd graphql-books
python -m venv .venv
source .venv/bin/activate
pip install "strawberry-graphql[fastapi]" "uvicorn[standard]"
Mendefinisikan Tipe dengan Strawberry
Strawberry memanfaatkan type hints Python untuk mendefinisikan schema GraphQL secara deklaratif — konsep yang mirip dengan validasi tipe data di Pydantic. Cukup buat class dengan decorator @strawberry.type, dan setiap atribut dengan anotasi tipe otomatis menjadi field GraphQL. Buat file main.py dengan isi berikut.
import strawberry
@strawberry.type
class Book:
id: strawberry.ID
title: str
author: str
year: int
rating: float
Class Book di atas otomatis menjadi tipe GraphQL bernama Book dengan lima field. Selanjutnya kita siapkan data contoh dan definisikan query-nya.
Membuat Query
Query adalah cara klien membaca data. Kita buat class Query dengan dua resolver: books untuk mengambil semua buku, dan book untuk mengambil satu buku berdasarkan ID.
from typing import List, Optional
books_db = [
Book(id="1", title="Clean Code", author="Robert C. Martin", year=2008, rating=4.5),
Book(id="2", title="The Pragmatic Programmer", author="David Thomas", year=1999, rating=4.6),
Book(id="3", title="Designing Data-Intensive Applications", author="Martin Kleppmann", year=2017, rating=4.8),
]
@strawberry.type
class Query:
@strawberry.field
def books(self) -> List[Book]:
return books_db
@strawberry.field
def book(self, id: strawberry.ID) -> Optional[Book]:
for b in books_db:
if b.id == id:
return b
return None
Membuat Mutation
Mutation dipakai untuk menulis data — membuat, memperbarui, atau menghapus. Di sini kita buat mutation addBook yang menambahkan buku baru ke dalam list dan mengembalikan buku yang baru dibuat.
@strawberry.type
class Mutation:
@strawberry.mutation
def add_book(self, title: str, author: str, year: int, rating: float) -> Book:
new_book = Book(
id=str(len(books_db) + 1),
title=title,
author=author,
year=year,
rating=rating,
)
books_db.append(new_book)
return new_book
schema = strawberry.Schema(query=Query, mutation=Mutation)
Menghubungkan ke FastAPI
Langkah terakhir: sambungkan schema ke aplikasi FastAPI lewat GraphQLRouter. Dengan begitu kita juga bisa menggabungkan endpoint GraphQL dengan endpoint REST biasa di aplikasi yang sama.
from fastapi import FastAPI
from strawberry.fastapi import GraphQLRouter
app = FastAPI(title="GraphQL Books API")
graphql_app = GraphQLRouter(schema)
app.include_router(graphql_app, prefix="/graphql")
Menjalankan Server dan Menguji dengan GraphiQL
Jalankan server dengan uvicorn.
uvicorn main:app --reload
Buka http://localhost:8000/graphql di browser. Kamu akan melihat GraphiQL, sebuah IDE interaktif bawaan untuk menulis dan menguji query. Coba jalankan query berikut untuk mengambil hanya title dan rating dari semua buku:
query {
books {
id
title
rating
}
}
GraphiQL juga menyediakan dokumentasi schema yang bisa dijelajah lewat tombol Docs. Kamu bisa melihat semua tipe, field, dan argumen yang tersedia tanpa harus membaca kode sumbernya — ini salah satu keunggulan GraphQL.
Untuk menambah data lewat mutation, jalankan query berikut:
mutation {
addBook(title: "Atomic Habits", author: "James Clear", year: 2018, rating: 4.7) {
id
title
}
}
Menambah Fitur Filter
GraphQL juga memudahkan penambahan fitur tanpa mengubah struktur endpoint. Kita tambahkan resolver searchBooks yang menerima argumen keyword untuk mencari buku berdasarkan judul atau penulis.
@strawberry.type
class Query:
@strawberry.field
def books(self) -> List[Book]:
return books_db
@strawberry.field
def book(self, id: strawberry.ID) -> Optional[Book]:
for b in books_db:
if b.id == id:
return b
return None
@strawberry.field
def search_books(self, keyword: str) -> List[Book]:
k = keyword.lower()
return [b for b in books_db if k in b.title.lower() or k in b.author.lower()]
Uji dengan query { searchBooks(keyword: "clean") { title author } }. Perhatikan bahwa menambah fitur seperti ini tidak memerlukan versi endpoint baru — klien yang sudah ada tetap berjalan normal.
Menuju Produksi
Contoh di atas menyimpan data di dalam list (in-memory), jadi data akan hilang saat server dimatikan. Untuk produksi, ganti penyimpanan dengan database sungguhan seperti PostgreSQL atau SQLite, dan pertimbangkan fitur autentikasi serta batasan kedalaman query agar API aman.
Kesimpulan
Kamu baru saja membangun API GraphQL lengkap dengan Python menggunakan Strawberry dan FastAPI: mendefinisikan tipe, membuat query dan mutation, menguji lewat GraphiQL, hingga menambah fitur filter. Dari sini kamu bisa melanjutkan dengan menambahkan autentikasi, integrasi database, atau mempelajari konsep lanjutan seperti DataLoader untuk menghindari masalah N+1. Selamat mencoba!