DataLens

Mengembalikan JSON dan membuat API sederhana

Buat REST API kecil yang menampilkan daftar, membaca, membuat, memperbarui, dan menghapus resource dengan JSON, URL yang konsisten, dan status code yang benar.

Level: Menengah · 10 menit baca

Latihan modul ini

Soal-soal untuk pelajaran ini berjalan di browser-mu dan langsung mengecek jawabanmu.

Idenya

Tidak semua client adalah orang dengan browser. Aplikasi mobile, dashboard Streamlit, sistem milik partner, atau JavaScript buatanmu sendiri semuanya menginginkan data, bukan HTML. API (Application Programming Interface) menjawab request dengan JSON, format yang sudah kamu parse di track Python.

Sebagian besar API web mengikuti konvensi bernama REST: URL menamai resource (benda), dan method HTTP menyatakan apa yang dilakukan terhadapnya.

Method + URLArtiStatus sukses
GET /api/productsdaftar produk200
GET /api/products/3membaca produk 3200 (404 kalau tidak ada)
POST /api/productsmembuat produk201
PATCH /api/products/3memperbarui sebagian field200
DELETE /api/products/3menghapusnya204 (tanpa body)

Perhatikan bahwa URL-nya berupa kata benda (/products), tidak pernah kata kerja (/getProducts, /deleteProduct): method-lah kata kerjanya.

Mengembalikan JSON

Kembalikan dict atau list, dan Flask mengubahnya menjadi JSON dengan Content-Type yang tepat:

from flask import Flask

app = Flask(__name__)
PRODUCTS = {
    1: {"id": 1, "name": "French Press 600ml", "price": 185000},
    2: {"id": 2, "name": "Pour-over Kettle", "price": 320000},
}


@app.get("/api/products")
def list_products():
    return {"products": list(PRODUCTS.values()), "count": len(PRODUCTS)}


r = app.test_client().get("/api/products")
print(r.status_code, r.mimetype)
print(r.get_json())
200 application/json
{'count': 2, 'products': [{'id': 1, 'name': 'French Press 600ml', 'price': 185000}, {'id': 2, 'name': 'Pour-over Kettle', 'price': 320000}]}

r.get_json() di test client mem-parse body kembali menjadi Python. (jsonify(...) sama saja dengan mengembalikan dict; kamu akan melihatnya di kode yang lebih lama.)

Membungkus list di dalam objek ({"products": [...], "count": 2}) adalah kebiasaan yang baik: kamu bisa menambah field nanti (paging, total) tanpa merusak client.

Membaca JSON dari request

Aplikasi mobile atau program lain yang berbicara dengan API-mu tidak mengisi formulir HTML. Mereka mengirim datanya sebagai JSON di body request. Untuk menambah produk lewat API, client mengirim sesuatu seperti {"name": "Moka Pot", "price": 320000}, dan request.get_json() mengubahnya menjadi dictionary Python:

from flask import Flask, request

app = Flask(__name__)
PRODUCTS = {}
next_id = 1


@app.post("/api/products")
def create_product():
    global next_id
    data = request.get_json(silent=True) or {}
    name = str(data.get("name", "")).strip()
    price = data.get("price")
    errors = []
    if not name:
        errors.append("name is required")
    if not isinstance(price, int) or price <= 0:
        errors.append("price must be a positive integer")
    if errors:
        return {"errors": errors}, 400
    product = {"id": next_id, "name": name, "price": price}
    PRODUCTS[next_id] = product
    next_id += 1
    return product, 201


client = app.test_client()
r = client.post("/api/products", json={"name": "Matcha 100g", "price": 85000})
print(r.status_code, r.get_json())
r = client.post("/api/products", json={"price": "cheap"})
print(r.status_code, r.get_json())
201 {'id': 1, 'name': 'Matcha 100g', 'price': 85000}
400 {'errors': ['name is required', 'price must be a positive integer']}

Membaca, memperbarui, menghapus

API produk biasanya butuh rangkaian operasi lengkap: melihat satu produk, mengubahnya, dan menghapusnya. Masing-masing memakai URL yang sama, /api/products/<id>, dengan HTTP method berbeda yang menyatakan apa yang harus dilakukan:

from flask import Flask, request, abort

app = Flask(__name__)
PRODUCTS = {1: {"id": 1, "name": "French Press 600ml", "price": 185000}}


def get_or_404(product_id):
    return PRODUCTS.get(product_id) or abort(404)


@app.get("/api/products/<int:product_id>")
def read_product(product_id):
    return get_or_404(product_id)


@app.patch("/api/products/<int:product_id>")
def update_product(product_id):
    product = get_or_404(product_id)
    data = request.get_json(silent=True) or {}
    if "price" in data:
        if not isinstance(data["price"], int) or data["price"] <= 0:
            return {"errors": ["price must be a positive integer"]}, 400
        product["price"] = data["price"]
    return product


@app.delete("/api/products/<int:product_id>")
def delete_product(product_id):
    get_or_404(product_id)
    del PRODUCTS[product_id]
    return "", 204


client = app.test_client()
print(client.patch("/api/products/1", json={"price": 175000}).get_json())
print(client.delete("/api/products/1").status_code)
print(client.get("/api/products/1").status_code)
{'id': 1, 'name': 'French Press 600ml', 'price': 175000}
204
404

204 No Content berarti "selesai, tidak ada yang perlu dikirim balik". Helper kecil seperti get_or_404 membuat setiap route yang butuh "produk ini atau 404" cukup satu baris.

Error JSON, bukan error HTML

Secara default, abort(404) mengembalikan halaman error HTML, yang tidak bisa dipakai client API. Daftarkan error handler yang mengembalikan JSON:

from flask import Flask, abort

app = Flask(__name__)


@app.errorhandler(404)
def not_found(error):
    return {"error": "not found"}, 404


@app.get("/api/products/<int:product_id>")
def read_product(product_id):
    abort(404)


r = app.test_client().get("/api/products/99")
print(r.status_code, r.mimetype, r.get_json())
404 application/json {'error': 'not found'}

Modul penanganan error membahas ini lebih dalam. Untuk sekarang: API harus menjawab setiap request, termasuk yang gagal, dalam JSON.

Ide utama

API yang baik itu mudah ditebak: kata benda untuk URL, method untuk aksi, JSON masuk dan keluar, status code yang benar, dan error berupa objek JSON dengan pesan yang jelas.

Mengapa ini penting untuk kode yang bersih

API adalah kontrak yang diandalkan kode milik orang lain. Konsistensi (bentuk yang sama, format error yang sama, status code yang sama di mana-mana) lebih penting daripada kecerdikan. Helper kecil (get_or_404, satu fungsi validasi per resource) menjaga setiap route tetap pendek, dan test client memungkinkanmu mengunci kontrak itu dengan test, yang dibahas beberapa modul lagi.

Rangkuman

Latihan modul ini

Soal-soal untuk pelajaran ini berjalan di browser-mu dan langsung mengecek jawabanmu.