Belajar Flask - REST API Design
Episode 14 of 27

Belajar Flask - REST API Design

Merancang REST API yang konsisten: konvensi resource dan HTTP methods, format JSON request/response, pagination untuk list data, API versioning, dan pola endpoint JSON yang siap dikonsumsi SPA dan mobile client.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Di episode-episode sebelumnya kita membangun endpoint JSON secara sporadis — setiap view memutuskan sendiri formatnya. Episode 14 merangkai semuanya menjadi desain API yang konsisten: konvensi resource, format response standar, pagination, dan versioning. Ini yang membedakan "kumpulan endpoint" dari REST API yang profesional.

Prinsipnya: REST API yang baik bisa dikonsumsi oleh siapa pun (SPA, mobile, integrasi) tanpa membaca source code — karena konvensi dan formatnya seragam.

Konvensi Resource dan Method

REST memodelkan data sebagai resource (/users, /posts, /items) yang dimanipulasi dengan HTTP methods. Konvensi standarnya:

MethodURLFungsiStatus sukses
GET/usersList users200
GET/users/42Detail user200
POST/usersBuat user baru201
PUT/users/42Update penuh user200
PATCH/users/42Update sebagian200
DELETE/users/42Hapus user200/204

Aturan penamaan: resource jamak (/users, bukan /user), URL berisi noun, bukan verb (/users/42, bukan /getUser). Aksi yang tidak cocok dengan CRUD (misal "ban user") memakai sub-resource: POST /users/42/ban atau POST /users/42/bans.

Format JSON Request/Response

Konsistensi format adalah setengah dari kualitas API. Satu pola response standar:

PythonFormat response standar
def api_ok(data, status: int = 200) -> tuple[dict, int]:
    return jsonify({"data": data}), status
 
 
def api_error(message: str, status: int) -> tuple[dict, int]:
    return jsonify({"error": message, "status": status}), status

Response sukses selalu membungkus payload di data; error selalu konsisten berbentuk {"error": ..., "status": ...} — lanjutan dari episode 5. Contoh endpoint:

PythonEndpoint GET /api/users
@api_bp.get("/users")
def list_users() -> tuple[dict, int]:
    users = db.session.scalars(db.select(User).order_by(User.id)).all()
    return api_ok([
        {"id": u.id, "username": u.username, "email": u.email}
        for u in users
    ])

Validasi input POST memakai request.get_json(silent=True) dan pola error 400 dari episode 5, atau bentuk yang lebih kaya lewat form/schema library (episode 12).

Pagination: List Data yang Besar

Mengembalikan seluruh baris tabel dalam satu response adalah anti-pattern: lambat dan membebani client. Solusinya pagination — potong data per halaman:

PythonPagination dengan Flask-SQLAlchemy
@api_bp.get("/users")
def list_users() -> tuple[dict, int]:
    page = request.args.get("page", 1, type=int)
    per_page = min(request.args.get("per_page", 20, type=int), 100)
 
    pagination = db.paginate(
        db.select(User).order_by(User.id), page=page, per_page=per_page
    )
 
    return api_ok({
        "items": [
            {"id": u.id, "username": u.username} for u in pagination.items
        ],
        "page": pagination.page,
        "per_page": pagination.per_page,
        "total": pagination.total,
        "pages": pagination.pages,
    })

db.paginate() menangani hitung total dan slicing. Selalu batasi per_page (di atas pakai min(..., 100)) agar satu request tidak bisa menarik seluruh tabel. Client berpindah halaman dengan GET /api/users?page=2&per_page=20.

Tip

Untuk API yang lebih canggih, pertimbangkan cursor pagination: halaman berikutnya direferensikan lewat token cursor (misal ?cursor=eyJpZCI6...), bukan nomor halaman. Lebih stabil terhadap data yang bertambah di tengah penelusuran. Nomor halaman sederhana cukup untuk mayoritas aplikasi — jangan over-engineer.

API Versioning

Saat API berevolusi, perubahan format bisa memecah client lama. Versioning memberi ruang bernapas:

Strategi versioning di URL
/api/v1/users
/api/v2/users

Di Flask, versi adalah blueprint terpisah:

PythonBlueprint per versi
api_v1_bp = Blueprint("api_v1", __name__, url_prefix="/api/v1")
api_v2_bp = Blueprint("api_v2", __name__, url_prefix="/api/v2")
 
# di factory:
app.register_blueprint(api_v1_bp)
app.register_blueprint(api_v2_bp)

Aturan praktis: v1 stabil selamanya; perubahan breaking masuk ke v2; fitur baru non-breaking ditambahkan ke versi aktif. Client yang belum siap migrasi tetap bekerja di v1 selama masa transisi.

Menjaga Konsistensi: Blueprint API Khusus

REST API sebaiknya dipisah dari halaman web — buat blueprint khusus api dengan error handler sendiri:

Pythonapi blueprint - error handler JSON
api_bp = Blueprint("api", __name__, url_prefix="/api")
 
 
@api_bp.app_errorhandler(404)
def api_not_found(e: Exception) -> tuple[dict, int]:
    return api_error("resource tidak ditemukan", 404)
 
 
@api_bp.app_errorhandler(400)
def api_bad_request(e: Exception) -> tuple[dict, int]:
    return api_error("request tidak valid", 400)

Dengan handler per blueprint (episode 9), halaman web bisa menampilkan error HTML sementara API selalu mengembalikan JSON.

Keamanan Dasar untuk API

Beberapa hal yang wajib sejak dini di API:

  • Autentikasi: endpoint yang menulis data wajib terproteksi — JWT (episode 18) atau session.
  • Rate limiting: batasi request per user/IP dengan Flask-Limiter (episode 15).
  • Validasi tipe data: jangan terima string di tempat yang harus int tanpa konversi yang aman.
  • Jangan bocorkan detail: error 500 jangan menyertakan traceback/SQL (episode 5).
  • CORS: jika dikonsumsi browser dari domain lain, atur flask-cors — jangan * tanpa berpikir.

Common Pitfalls REST API

  • Verb di URL (/getUser, /deletePost): pakai method + noun resource.
  • Response tidak konsisten: campur bentuk {data: ...} dan {items: ...} di endpoint berbeda.
  • Tanpa pagination: satu endpoint menarik 100 ribu baris → timeout dan server terbebani.
  • Error hanya status code: sertakan pesan yang bisa dipakai client untuk menampilkan UI.
  • Tanpa versioning: perubahan format = client patah semua.

Penutup

Pada episode 14 ini, kalian telah merancang REST API yang konsisten dan siap produksi.

Inti yang harus dibawa pulang:

  • Resource jamak + HTTP methods; URL noun, bukan verb.
  • Format response seragam: {"data": ...} sukses, {"error": ..., "status": ...} gagal.
  • Pagination dengan db.paginate() untuk list data; batasi per_page.
  • API versioning via blueprint url_prefix (/api/v1, /api/v2).
  • API dan web dipisah blueprint; masing-masing punya error handler sendiri.

Di episode 15 selanjutnya, kita melindungi dan mempercepat API: caching & rate limiting — Flask-Caching dengan Redis, per-view caching, Flask-Limiter dengan limit per-user/IP, dan kombinasi cache response + proteksi API. Sampai jumpa di episode 15!

Belajar Flask - REST API Design | Belajar Flask