Flask API JSON: Request Body, jsonify, dan Status Code
Flask bisa mengembalikan halaman HTML, tetapi ia juga nyaman dipakai sebagai API untuk frontend JavaScript, mobile app, atau service lain. Dalam API, client tidak membutuhkan layout. Client membutuhkan data dengan bentuk yang stabil, status code yang tepat, dan error yang bisa diproses tanpa menebak isi string.
Kita akan membuat endpoint create dan detail note. Keduanya memakai model Note yang tersimpan di database, lalu membungkus hasilnya dalam key data.
Bentuk Response yang Konsisten
Tentukan pola sebelum endpoint bertambah banyak. Response sukses dari contoh ini berbentuk:
{
"data": {
"id": 1,
"title": "Belajar Flask API"
}
}
Sementara itu, response gagal memakai object error:
{
"error": {
"code": "validation_error",
"message": "Field title wajib diisi."
}
}
Nama key tidak harus persis seperti ini. Konsistensi lebih penting daripada memilih satu format yang dianggap paling sempurna.
Buat Endpoint Create dan Detail
# File: routes/api.py
from flask import Blueprint, jsonify, request
from models import Note, db
api_bp = Blueprint("api", __name__, url_prefix="/api")
def note_data(note):
return {"id": note.id, "title": note.title}
@api_bp.post("/notes")
def create_note():
if not request.is_json:
return jsonify({
"error": {
"code": "unsupported_media_type",
"message": "Gunakan Content-Type application/json.",
}
}), 415
payload = request.get_json(silent=True)
if not isinstance(payload, dict):
return jsonify({
"error": {
"code": "invalid_json",
"message": "Body JSON harus berupa object.",
}
}), 400
title = payload.get("title")
if not isinstance(title, str) or not title.strip():
return jsonify({
"error": {
"code": "validation_error",
"message": "Field title harus berupa teks dan wajib diisi.",
}
}), 422
title = title.strip()
note = Note(title=title)
db.session.add(note)
db.session.commit()
return jsonify({"data": note_data(note)}), 201
@api_bp.get("/notes/<int:note_id>")
def get_note(note_id):
note = db.session.get(Note, note_id)
if note is None:
return jsonify({
"error": {
"code": "not_found",
"message": "Note tidak ditemukan.",
}
}), 404
return jsonify({"data": note_data(note)})
Helper note_data() mengubah object model menjadi dictionary yang aman dikirim. Jangan langsung mengirim seluruh __dict__ model karena isinya bisa mencakup state internal atau field sensitif yang tidak seharusnya keluar dari API.
Periksa Content Type Sebelum Membaca Body
Client yang mengirim JSON seharusnya menyertakan header Content-Type: application/json. request.is_json memeriksa media type tersebut. Jika header-nya salah, endpoint mengembalikan 415 Unsupported Media Type dan menjelaskan format yang diharapkan.
Setelah itu, request.get_json(silent=True) mencoba mengurai body. Opsi silent=True membuat Flask mengembalikan None jika JSON rusak, sehingga kita dapat menyusun error sendiri. Pengecekan isinstance(payload, dict) juga menolak array atau nilai JSON lain karena endpoint ini mengharapkan object dengan field title.
Sebelum memanggil .strip(), kode memastikan title memang bertipe string. Ini penting karena JSON juga dapat membawa angka, boolean, array, atau object. Tanpa pengecekan tipe, payload seperti {"title": 123} justru memicu error internal saat angka diperlakukan sebagai string.
Kenapa Status Code-nya Berbeda?
201 Createdberarti note baru berhasil dibuat.400 Bad Requestdipakai ketika body JSON tidak bisa dibaca sesuai bentuk yang diminta.404 Not Foundberarti format URL benar, tetapi note dengan ID tersebut tidak ada.415 Unsupported Media Typeberarti client mengirim format body yang tidak didukung.422 Unprocessable Contentberarti JSON terbaca, tetapi nilai field tidak lolos validasi.
Status code membantu client memilih tindakan bahkan sebelum membaca detail pesan. Frontend dapat menandai field pada 422, menampilkan halaman tidak ditemukan pada 404, atau memperbaiki header request pada 415.
Uji API dengan curl
Kirim note yang valid:
# File: terminal
curl -i -X POST "http://127.0.0.1:5000/api/notes" \
-H "Content-Type: application/json" \
-d '{"title":"Belajar Flask API"}'
Lalu minta detail note yang baru dibuat:
# File: terminal
curl -i "http://127.0.0.1:5000/api/notes/1"
Opsi -i menampilkan header response sehingga status code dan Content-Type terlihat. Coba ulangi request tanpa header, dengan body {}, dengan title berupa angka, dan dengan ID yang tidak ada.
Jangan Campur Response HTML dan JSON
Jika route API gagal dan Flask mengembalikan halaman error HTML, client JSON bisa kesulitan memprosesnya. Tambahkan error handler khusus blueprint atau aplikasi agar error yang memang ditujukan untuk /api tetap memakai format JSON.
Setelah create dan detail berjalan, tambahkan endpoint list, update, dan delete dengan pola response yang sama. Begitu client dapat memprediksi bentuk sukses maupun gagal, API akan terasa jauh lebih nyaman dipakai meskipun jumlah endpoint bertambah.
Artikel Terkait
Lanjutkan membaca topik yang masih satu konteks.
Flask Authentication: Login, Session, dan Logout
Buat authentication Flask dengan password hash, login session, current user, protected route, dan logout tanpa menyimpan password mentah.
Flask CRUD SQLAlchemy: Create, Read, Update, Delete
Bangun CRUD Flask dan SQLAlchemy untuk membuat, membaca, mengubah, serta menghapus note lengkap dengan validasi, form, dan redirect.
Flask SQLAlchemy: Koneksi Database dan Model
Hubungkan Flask ke SQLite dengan Flask-SQLAlchemy, buat model dan tabel, lalu pahami proses add, commit, serta query data.
Struktur Project Flask: Blueprint, Template, dan Config
Rapikan struktur project Flask dengan application factory, Blueprint, folder route, template, static files, dan config yang terpisah.