This episode builds a real API: comparing Flask, Django, and FastAPI and when to choose each one. You'll also learn to design RESTful and GraphQL APIs, including versioning, pagination, and input validation.

After mastering networking in episode 12, now it's time to build the server side. Episode 13 compares the three most popular Python web frameworks — Flask, Django, and FastAPI — and teaches you how to design good APIs.
We'll build a real FastAPI application, design RESTful and GraphQL, and apply versioning, pagination, and input validation — skills you'll use directly in the workplace.
Flask is minimal and flexible, great for small APIs and prototypes:
pip install flaskfrom flask import Flask, jsonify
app = Flask(__name__)
@app.route("/halo")
def halo():
return jsonify({"pesan": "Halo Flask"})@app.route("/halo") connects a URL to a function. Flask gives you full freedom to choose components, but the structure responsibility is on you. Good for small projects that need total control.
Django provides everything — ORM, admin, auth — in a single framework:
pip install django
django-admin startproject belajardjango
cd belajardjango
python3 manage.py runserverdjango-admin startproject belajardjango creates a project with a complete structure. Django excels at large web applications that need many built-in features: admin panel, authentication, and migrations come for free.
FastAPI combines Python types, automatic validation, and async performance:
pip install "fastapi[standard]"from fastapi import FastAPI
app = FastAPI()
@app.get("/halo")
def halo():
return {"pesan": "Halo FastAPI"}@app.get("/halo") defines an endpoint. FastAPI uses Python types for validation and automatic documentation. It's the top choice for new APIs and microservices in the modern era.
REST (Representational State Transfer) uses resources and HTTP methods:
from fastapi import FastAPI
app = FastAPI()
produk = {}
@app.get("/produk")
def daftar_produk():
return produk
@app.post("/produk/{id_produk}")
def tambah_produk(id_produk: int, nama: str):
produk[id_produk] = nama
return {"id": id_produk, "nama": nama}@app.get("/produk") retrieves the list, @app.post("/produk/{id_produk}") adds data. REST uses nouns for resources and HTTP methods as actions. Endpoint names should be plural and consistent.
Input validation prevents corrupt data from entering the system and is a security foundation. FastAPI uses pydantic for this:
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Produk(BaseModel):
nama: str = Field(min_length=1, max_length=100)
harga: int = Field(gt=0)
@app.post("/produk")
def buat_produk(data: Produk):
return dataclass Produk(BaseModel) defines a schema with constraints. Field(min_length=1, max_length=100) and Field(gt=0) validate automatically. If input is invalid, FastAPI returns 422 with error details — no need to write manual validation code.
APIs change over time. Versioning protects existing clients:
from fastapi import FastAPI
app_v1 = FastAPI()
app_v2 = FastAPI()
@app_v1.get("/produk")
def daftar_v1():
return {"versi": "v1"}
@app_v2.get("/produk")
def daftar_v2():
return {"versi": "v2", "paginated": True}Creating a separate app per version is one approach. Another is using URL prefixes /api/v1 and /api/v2. Versioning gives clients time to migrate as the API evolves.
GraphQL lets clients request the specific data they need, unlike REST which returns a fixed structure:
import strawberry
@strawberry.type
class Produk:
id: int
nama: str
@strawberry.type
class Query:
@strawberry.field
def produk(self, id: int) -> Produk:
return Produk(id=id, nama="Contoh")
schema = strawberry.Schema(query=Query)
print(schema.as_str()[:50])@strawberry.type defines a GraphQL type and @strawberry.field defines a query. Clients can pick the fields they ask for, reducing over-fetching. GraphQL suits applications with many clients and diverse data needs.
Key takeaways:
In the next episode, episode 14, we'll cover security best practices — secure coding with input validation and escaping, injection prevention, authentication and authorization patterns with JWT and OAuth2, CSRF and CORS protection, and dependency security scanning. Your API will become secure!