Belajar Python Membuat REST API Flask

flask adalah framework yang saya pilih ketika ingin membuat API kecil dengan cepat, satu file sudah cukup untuk menjalankan server. untuk install library python, pastikan sudah paham pip dan virtual environment dulu. panduan ini adalah alur yang selalu saya pakai untuk memulai: setup, route pertama, JSON response, lalu koneksi database. semuanya bisa diikuti tanpa perlu memahami framework besar dulu.
1. apa itu flask?
flask adalah micro-framework Python untuk web. dikategorikan “micro” karena:
- inti kecil, banyak fitur harus ditambah via ekstensi.
- tidak ada ORM bawaan, pakai SQLAlchemy via Flask-SQLAlchemy.
- tidak ada admin panel bawaan, beda dengan Django.
- tidak ada auth bawaan, pakai Flask-Login, Flask-JWT, dll.
keuntungan:
- simpel : pemula bisa paham dalam 1 jam.
- fleksibel : pilih sendiri library yang mau dipakai.
- banyak ekstensi : hampir semua kebutuhan ada ekstensi Flask.
kekurangan:
- harus pilih banyak library manual : decision fatigue.
- kurang cocok untuk app besar : Django atau FastAPI lebih terstruktur.
2. install flask
pip install flask flask-sqlalchemy
library yang di-install:
flask: framework utama.flask-sqlalchemy: ORM SQLAlchemy integrasi Flask.
opsional tapi berguna:
flask-cors: handle CORS untuk frontend.flask-jwt-extended: JWT auth.flask-marshmallow: serialization & validation.flask-migrate: database migration seperti Django.
3. app minimal: hello world
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/api/hello")
def hello():
return jsonify({"message": "Hello World!"})
if __name__ == "__main__":
app.run(debug=True)
simpan sebagai app.py, jalankan:
python app.py
output:
* Running on http://127.0.0.1:5000
buka browser ke http://127.0.0.1:5000/api/hello atau test dengan curl:
curl http://127.0.0.1:5000/api/hello
response:
{"message":"Hello World!"}
penjelasan per baris:
from flask import Flask, jsonify: import class Flask dan fungsi jsonify.app = Flask(__name__): buat instance Flask.__name__memberi tahu Flask di mana mencari template/static.@app.route("/api/hello"): decorator yang daftarkan fungsihelloke URL/api/hello.def hello():: fungsi yang dijalankan saat URL diakses.return jsonify({...}): return response JSON.jsonifyset Content-Type keapplication/jsondan serialize dict ke JSON string.if __name__ == "__main__":: hanya jalan kalau file dijalankan langsung (bukan di-import).app.run(debug=True): start development server.debug=Trueauto-reload saat file berubah.
4. CRUD lengkap dengan SQLAlchemy
contoh API CRUD (Create, Read, Update, Delete) untuk resource items:
from flask import Flask, jsonify, request
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///data.db"
db = SQLAlchemy(app)
class Item(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), nullable=False)
price = db.Column(db.Float, nullable=False)
def to_dict(self):
return {"id": self.id, "name": self.name, "price": self.price}
with app.app_context():
db.create_all()
@app.route("/api/items", methods=["GET"])
def get_items():
items = Item.query.all()
return jsonify([item.to_dict() for item in items])
@app.route("/api/items/<int:id>", methods=["GET"])
def get_item(id):
item = Item.query.get_or_404(id)
return jsonify(item.to_dict())
@app.route("/api/items", methods=["POST"])
def create_item():
data = request.get_json()
item = Item(name=data["name"], price=data["price"])
db.session.add(item)
db.session.commit()
return jsonify(item.to_dict()), 201
@app.route("/api/items/<int:id>", methods=["PUT"])
def update_item(id):
item = Item.query.get_or_404(id)
data = request.get_json()
item.name = data.get("name", item.name)
item.price = data.get("price", item.price)
db.session.commit()
return jsonify(item.to_dict())
@app.route("/api/items/<int:id>", methods=["DELETE"])
def delete_item(id):
item = Item.query.get_or_404(id)
db.session.delete(item)
db.session.commit()
return "", 204
if __name__ == "__main__":
app.run(debug=True)
penjelasan per bagian:
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///data.db": set URL database.sqlite:///data.dbartinya filedata.dbdi folder project.db = SQLAlchemy(app): inisialisasi SQLAlchemy dengan Flask app.class Item(db.Model):: model untuk tabel items. inherit daridb.Model.db.Column(db.Integer, primary_key=True): kolom id integer, primary key.nullable=False: kolom wajib diisi, tidak boleh NULL.def to_dict(self):: method untuk konversi object ke dict (untuk JSON response).with app.app_context(): db.create_all(): buat tabel di database kalau belum ada. perlu app_context karena Flask pakai app context untuk operasi DB.methods=["GET"]: endpoint hanya untuk method GET. default hanya GET.Item.query.all(): ambil semua item dari tabel.Item.query.get_or_404(id): ambil item by id, return 404 kalau tidak ada.request.get_json(): parse body request sebagai JSON dict.data["name"]: akses key, raise KeyError kalau tidak ada.data.get("name", item.name): akses key dengan default value kalau tidak ada.db.session.add(item): tambahkan object ke session.db.session.commit(): simpan perubahan ke database.return jsonify(...), 201: return JSON dengan status code 201 (Created).return "", 204: return kosong dengan status code 204 (No Content, success delete).
5. testing API dengan curl
# GET semua items
curl http://localhost:5000/api/items
# POST item baru
curl -X POST http://localhost:5000/api/items \
-H "Content-Type: application/json" \
-d '{"name":"Laptop","price":15000000}'
# PUT update item id=1
curl -X PUT http://localhost:5000/api/items/1 \
-H "Content-Type: application/json" \
-d '{"price":14000000}'
# DELETE item id=1
curl -X DELETE http://localhost:5000/api/items/1
penjelasan per perintah:
curl -X POST: kirim request dengan method POST.-H "Content-Type: application/json": set header Content-Type. server butuh ini untuk parse body sebagai JSON.-d '{...}': data body. JSON harus string valid.
alternatif: pakai Postman (GUI) atau Insomnia untuk test yang lebih nyaman.
6. validasi input dengan marshmallow
input user harus divalidasi. flask-marshmallow kasih cara yang rapi:
pip install flask-marshmallow marshmallow-sqlalchemy
from flask import Flask, jsonify, request
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
from marshmallow import validate
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///data.db"
db = SQLAlchemy(app)
ma = Marshmallow(app)
class Item(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), nullable=False)
price = db.Column(db.Float, nullable=False)
class ItemSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Item
load_instance = True
# Tambah validasi custom
name = ma.auto_field(required=True, validate=validate.Length(min=1, max=80))
price = ma.auto_field(required=True, validate=validate.Range(min=0))
item_schema = ItemSchema()
items_schema = ItemSchema(many=True)
@app.route("/api/items", methods=["POST"])
def create_item():
try:
item = item_schema.load(request.get_json())
except ValidationError as err:
return jsonify({"errors": err.messages}), 400
db.session.add(item)
db.session.commit()
return jsonify(item_schema.dump(item)), 201
item_schema.load(data) otomatis validasi tipe data dan aturan. kalau gagal, return 400 dengan pesan error.
7. error handling yang konsisten
return JSON yang konsisten untuk setiap error, bukan HTML default Flask:
from flask import Flask, jsonify
from werkzeug.exceptions import HTTPException
app = Flask(__name__)
@app.errorhandler(404)
def not_found(e):
return jsonify({"error": "Resource not found", "status": 404}), 404
@app.errorhandler(400)
def bad_request(e):
return jsonify({"error": "Bad request", "status": 400}), 400
@app.errorhandler(500)
def internal_error(e):
# Log error untuk debug
app.logger.exception("Internal server error")
return jsonify({"error": "Internal server error", "status": 500}), 500
# Handle semua HTTPException lainnya
@app.errorhandler(HTTPException)
def handle_http_error(e):
return jsonify({"error": e.description, "status": e.code}), e.code
8. auth dengan JWT
untuk proteksi endpoint, pakai JWT (JSON Web Token):
pip install flask-jwt-extended
from flask import Flask, jsonify, request
from flask_jwt_extended import JWTManager, create_access_token, jwt_required, get_jwt_identity
app = Flask(__name__)
app.config["JWT_SECRET_KEY"] = "ganti-dengan-random-secret-key-yang-panjang"
jwt = JWTManager(app)
@app.route("/api/login", methods=["POST"])
def login():
data = request.get_json()
username = data.get("username")
password = data.get("password")
# Validasi user (contoh, jangan hardcode di production)
if username == "admin" and password == "rahasia123":
token = create_access_token(identity=username)
return jsonify({"token": token}), 200
return jsonify({"error": "Username atau password salah"}), 401
@app.route("/api/profile", methods=["GET"])
@jwt_required()
def profile():
current_user = get_jwt_identity()
return jsonify({"user": current_user})
test dengan curl:
# Login
curl -X POST http://localhost:5000/api/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"rahasia123"}'
# Response: {"token":"eyJhbGc..."}
# Akses endpoint protected
curl -H "Authorization: Bearer eyJhbGc..." \
http://localhost:5000/api/profile
JWT_SECRET_KEY wajib ganti di production. generate dengan python -c "import secrets; print(secrets.token_hex(32))".
9. CORS untuk frontend
kalau frontend (misal di localhost:3000) mau akses API Flask (di localhost:5000), browser akan blokir karena CORS policy. fix dengan flask-cors:
pip install flask-cors
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app) # izinkan semua origin (dev only!)
# Atau spesifik origin untuk production
CORS(app, resources={r"/api/*": {"origins": ["https://myapp.com"]}})
10. struktur folder untuk project menengah
untuk project serius, jangan taruh semua kode di satu file. struktur yang umum:
flask-api/
├── app/
│ ├── __init__.py # factory pattern
│ ├── models.py # SQLAlchemy models
│ ├── routes/
│ │ ├── __init__.py
│ │ ├── items.py
│ │ └── auth.py
│ ├── schemas.py # Marshmallow schemas
│ └── extensions.py # db, ma, jwt, cors
├── config.py # konfigurasi
├── run.py # entry point
├── requirements.txt
└── .env # environment variables
app/__init__.py : factory pattern:
from flask import Flask
from .extensions import db, ma, jwt, cors
def create_app(config_class="config.Config"):
app = Flask(__name__)
app.config.from_object(config_class)
# Init ekstensi
db.init_app(app)
ma.init_app(app)
jwt.init_app(app)
cors.init_app(app)
# Register blueprint
from .routes.items import items_bp
from .routes.auth import auth_bp
app.register_blueprint(items_bp, url_prefix="/api/items")
app.register_blueprint(auth_bp, url_prefix="/api/auth")
return app
blueprint memungkinkan split route ke beberapa file. lebih rapi untuk tim.
11. testing otomatis dengan pytest
test API otomatis pakai pytest:
pip install pytest
# test_app.py
import pytest
from app import create_app
from app.extensions import db
@pytest.fixture
def app():
app = create_app("config.TestConfig")
with app.app_context():
db.create_all()
yield app
db.drop_all()
@pytest.fixture
def client(app):
return app.test_client()
def test_get_items_empty(client):
response = client.get("/api/items")
assert response.status_code == 200
assert response.json == []
def test_create_item(client):
response = client.post("/api/items", json={
"name": "Laptop",
"price": 15000000
})
assert response.status_code == 201
assert response.json["name"] == "Laptop"
assert response.json["price"] == 15000000
def test_get_item_not_found(client):
response = client.get("/api/items/999")
assert response.status_code == 404
jalankan:
pytest
12. deployment production
jangan pakai flask run atau app.run() di production. pakai production-grade WSGI server.
pakai Gunicorn
pip install gunicorn
# Jalanin 4 worker, bind ke port 8000
gunicorn -w 4 -b 0.0.0.0:8000 "app:create_app()"
pakai Gunicorn + Nginx (recommended)
konfigurasi Nginx (/etc/nginx/sites-available/api):
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
deploy ke platform
opsi simpel tanpa setup server:
- Railway :
railway up, otomatis detect Flask. - Render : connect GitHub repo, otomatis build & deploy.
- Fly.io :
fly launchdanfly deploy. - Vercel : support Flask via Serverless Functions.
- Heroku :
git push heroku maindenganProcfile.
13. pagination, filtering & config modern 2026
GET /api/items tanpa paginasi akan berat kalau data sudah ribuan. tambah paginasi + filter + config .env:
# config.py
import os
class Config:
SQLALCHEMY_DATABASE_URI = os.getenv("DATABASE_URL", "sqlite:///data.db")
SQLALCHEMY_TRACK_MODIFICATIONS = False # wajib False, cegah memory leak
SQLALCHEMY_ENGINE_OPTIONS = {"pool_pre_ping": True, "pool_recycle": 3600}
# routes/items.py
from flask import request, jsonify
@app.route("/api/items", methods=["GET"])
def get_items():
page = request.args.get("page", 1, type=int)
per_page = min(request.args.get("per_page", 20, type=int), 100)
q = request.args.get("q", "")
query = Item.query
if q:
query = query.filter(Item.name.ilike(f"%{q}%"))
pagination = query.paginate(page=page, per_page=per_page, error_out=False)
return jsonify({
"items": [i.to_dict() for i in pagination.items],
"total": pagination.total,
"page": pagination.page,
"per_page": pagination.per_page
})
untuk SQLAlchemy 2.x + Flask-SQLAlchemy 3.1 (2024+), gunakan typed Mapped[] kalau mau type-check mypy/pyright:
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
class Base(DeclarativeBase): pass
db = SQLAlchemy(model_class=Base)
class Item(db.Model):
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(db.String(80))
pakai python-dotenv untuk load .env otomatis di dev: pip install python-dotenv lalu from dotenv import load_dotenv; load_dotenv().
14. rate limiting, OpenAPI & security
pip install Flask-Limiter flask-smorest
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
limiter = Limiter(get_remote_address, app=app, default_limits=["100/hour"])
@app.route("/api/items", methods=["POST"])
@limiter.limit("10/minute")
def create_item(): ...
# OpenAPI Swagger otomatis (Flask-SMorest)
from flask_smorest import Api
api = Api(app)
api.spec.title = "My API"
# buka /swagger-ui untuk docs interaktif
security checklist 2026: CORS(app, resources={r"/api/*": {"origins": ["https://myapp.com"]}}) jangan * di production, validasi input, X-RateLimit headers, dan generate SECRET_KEY via secrets.
15. tips produksi
- set
debug=Falsedi production.debug=Truekasih info stack trace yang bisa bocor ke user. Flask 3.1 butuh Python 3.9+ (3.8 sudah drop). - secret key di environment variable, jangan hardcode.
- HTTPS wajib untuk production, pakai Let’s Encrypt gratis.
- rate limiting pakai Flask-Limiter untuk cegah abuse (lihat bab 14).
- logging ke file atau service (Sentry, DataDog).
- monitoring dengan Prometheus + Grafana.
- database connection pool di SQLAlchemy untuk handle concurrent request (
pool_pre_ping=Trueatasi MySQL has gone away). - migrasi pakai
Flask-Migrate(Alembic):flask db migrate -m "add table" && flask db upgrade— jalankanupgradesekali di deploy, bukan per server.
Kesimpulan
- Flask micro-framework Python untuk API, ringan dan simpel
flask runhanya untuk development, production pakai Gunicorn + Nginx- SQLAlchemy untuk ORM,
flask-marshmallowuntuk validasi & serialization - JWT untuk auth,
flask-corsuntuk izinkan akses dari frontend - struktur folder blueprint pattern untuk project menengah-besar
- test API otomatis dengan pytest dan
test_client() - deploy ke Railway/Render/Fly.io untuk simpel, atau setup Gunicorn + Nginx untuk control penuh
kalau API mulai kompleks dan butuh async, pertimbangkan FastAPI : performance-nya lebih cepet untuk high-concurrency. tapi untuk kebanyakan project, Flask cukup.
Baca Juga Mengenai :
Pertanyaan yang Sering Diajukan
Apa itu Flask dan kenapa pakai Flask untuk API?
flask adalah micro-framework Python untuk web. ringan, simpel, dan fleksibel. cocok untuk REST API kecil-menengah karena tidak perlu setup banyak. untuk API besar, pertimbangkan FastAPI atau Django.
Bedanya Flask dan Django untuk API?
flask micro-framework, banyak hal harus ditambah manual (ORM, auth, admin). django full-featured, sudah include ORM, admin, auth bawaan. flask cocok untuk API sederhana, django untuk aplikasi besar dengan banyak fitur.
Bagaimana cara test API Flask?
pakai curl di terminal, Postman (GUI), atau pytest untuk automated test. flask punya test_client() bawaan untuk unit test API endpoint tanpa harus start server.
Bagaimana cara handle error di Flask API?
pakai @app.errorhandler(404) atau abort() untuk trigger error. custom error handler return JSON, bukan HTML default Flask. selalu log error untuk debug production.
Apakah Flask support async?
flask 2.0+ punya dukungan async/await, tapi untuk performa high-concurrency lebih baik pakai FastAPI atau Starlette. flask cocok untuk request sync tradisional.
Bagaimana deploy Flask API ke production?
jangan pakai flask run di production. pakai Gunicorn atau uWSGI sebagai WSGI server, di belakang Nginx reverse proxy. untuk deployment simpel, Railway, Render, atau Fly.io support Flask out of the box.
Cara validasi input JSON di Flask?
bisa manual dengan if-else, atau pakai library marshmallow atau pydantic. flask punya ekstensi flask-pydantic atau flask-marshmallow untuk validasi berbasis schema.
Bagaimana cara implementasi JWT auth di Flask?
pakai library flask-jwt-extended. simpan secret key di environment variable, generate token saat login, dan pakai @jwt_required() di endpoint yang butuh autentikasi.

