Project: REST API
Introduction
This project exposes a JSON API for posts under /api/v1/, protected write operations with JWT, and CORS for a separate frontend. You practice unified error responses, versioning, and tools like curl or Postman—patterns used by SPAs and mobile clients.
Prerequisites
- Responses and JSON API
- JWT API Authentication (Optional)
- CORS and Frontend Integration
- Flask-SQLAlchemy and Models
- Testing Flask Applications
Project Goals
GET /api/v1/posts— list (public)GET /api/v1/posts/<id>— detail (public)POST /api/v1/posts— create (JWT required)PUT /api/v1/posts/<id>— update (JWT required)DELETE /api/v1/posts/<id>— delete (JWT required)- Consistent JSON envelope:
{code, message, data}
Step 1: API Blueprint
app/api/routes.py:
from flask import Blueprint, request, jsonify
from flask_jwt_extended import jwt_required, get_jwt_identity
from app.extensions import db
from app.models import Post, User
api_v1_bp = Blueprint("api_v1", __name__, url_prefix="/api/v1")
def ok(data=None, message="success", code=0):
return jsonify({"code": code, "message": message, "data": data})
def err(message, http_status=400, code=1):
return jsonify({"code": code, "message": message, "data": None}), http_statusRegister in create_app and configure Flask-CORS for /api/*.
Step 2: Serialize Post
def post_to_dict(post):
return {
"id": post.id,
"title": post.title,
"body": post.body,
"created_at": post.created_at.isoformat() + "Z",
}Keep serializers in app/api/serializers.py as the API grows.
Step 3: List and Detail
Step 4: Auth Endpoints
@api_v1_bp.route("/login", methods=["POST"])
def login():
data = request.get_json(silent=True) or {}
user = User.query.filter_by(username=data.get("username", "")).first()
if user is None or not user.check_password(data.get("password", "")):
return err("invalid credentials", 401, code=401)
from flask_jwt_extended import create_access_token
token = create_access_token(identity=str(user.id))
return ok({"access_token": token, "token_type": "Bearer"})Protect mutations:
Add author_id on Post linked to User if you need per-author ownership checks.
Step 5: Update and Delete
Step 6: Test With curl
Step 7: pytest Smoke Tests
tests/test_api.py:
def test_list_posts(client):
r = client.get("/api/v1/posts")
assert r.status_code == 200
assert r.get_json()["code"] == 0
def test_create_requires_auth(client):
r = client.post("/api/v1/posts", json={"title": "x", "body": "y"})
assert r.status_code == 401Step 8: Frontend Handoff
Document base URL http://127.0.0.1:5000/api/v1 for React/Vue devs. Enable CORS for http://localhost:5173—see CORS chapter.
Optional OpenAPI with flask-smorest—compare SpringDoc on Spring Boot track.
Verification Checklist
- Public GET works without token
- POST without token returns 401
- JSON envelope consistent on success and error
- CORS preflight succeeds from dev frontend origin
- pytest passes for list and auth-required create
FAQ
204 response body?
Empty body is correct for DELETE success.
Pagination defaults?
Document page and per_page in a README for API consumers.
Flask-Login instead of JWT?
Possible for same-origin apps—JWT fits SPA chapter goals.
Rate limiting?
Add Flask-Limiter on /login—security chapter.
Version v2?
Register second blueprint url_prefix="/api/v2" without breaking v1.
Postman collection?
Export requests from Postman for teammates—optional deliverable.