Module 8 ยท Lesson 24

Building REST APIs with Flask

๐Ÿ Pythonโฑ 14 min read๐Ÿ“– Practical

REST API Design

REST (Representational State Transfer) is an architectural style for web APIs. RESTful APIs use HTTP methods (GET, POST, PUT, DELETE) and status codes meaningfully.

MethodActionExampleStatus
GETReadGET /users/1200 OK
POSTCreatePOST /users201 Created
PUTReplacePUT /users/1200 OK
PATCHUpdatePATCH /users/1200 OK
DELETEDeleteDELETE /users/1204 No Content
from flask import Flask, jsonify, request, abort app = Flask(__name__) # In-memory "database" for this example users = { 1: {"id": 1, "name": "Alice", "email": "alice@example.com"}, 2: {"id": 2, "name": "Bob", "email": "bob@example.com"}, } next_id = 3 # GET /users โ€” list all @app.route("/users", methods=["GET"]) def get_users(): return jsonify({"users": list(users.values())}) # GET /users/<id> โ€” get one @app.route("/users/<int:user_id>", methods=["GET"]) def get_user(user_id): user = users.get(user_id) if user is None: abort(404) return jsonify(user) # POST /users โ€” create @app.route("/users", methods=["POST"]) def create_user(): global next_id data = request.get_json() if not data or "name" not in data or "email" not in data: abort(400) # Bad Request user = {"id": next_id, "name": data["name"], "email": data["email"]} users[next_id] = user next_id += 1 return jsonify(user), 201 # PUT /users/<id> โ€” replace @app.route("/users/<int:user_id>", methods=["PUT"]) def update_user(user_id): if user_id not in users: abort(404) data = request.get_json() users[user_id].update(data) return jsonify(users[user_id]) # DELETE /users/<id> @app.route("/users/<int:user_id>", methods=["DELETE"]) def delete_user(user_id): if user_id not in users: abort(404) del users[user_id] return "", 204

Input Validation and Error Handling

from functools import wraps def validate_json(*required_fields): """Decorator to validate required JSON fields.""" def decorator(func): @wraps(func) def wrapper(*args, **kwargs): data = request.get_json() if not data: return jsonify({"error": "Request body must be JSON"}), 400 missing = [f for f in required_fields if f not in data] if missing: return jsonify({"error": f"Missing fields: {missing}"}), 400 return func(*args, **kwargs) return wrapper return decorator @app.route("/users", methods=["POST"]) @validate_json("name", "email") def create_user(): data = request.get_json() # We know name and email exist here ... # Consistent error responses @app.errorhandler(400) def bad_request(e): return jsonify({"error": "Bad request", "message": str(e)}), 400 @app.errorhandler(404) def not_found(e): return jsonify({"error": "Not found"}), 404 @app.errorhandler(500) def server_error(e): return jsonify({"error": "Internal server error"}), 500

Authentication and Blueprints

# Simple API key auth from functools import wraps def require_api_key(func): @wraps(func) def wrapper(*args, **kwargs): api_key = request.headers.get("X-API-Key") if api_key != app.config.get("API_KEY"): return jsonify({"error": "Unauthorized"}), 401 return func(*args, **kwargs) return wrapper @app.route("/protected") @require_api_key def protected(): return jsonify({"data": "secret"}) # Blueprints โ€” organize routes into modules from flask import Blueprint users_bp = Blueprint("users", __name__, url_prefix="/api/v1") @users_bp.route("/users") def get_users(): return jsonify([]) # Register in app factory app.register_blueprint(users_bp)

Key Takeaways

Practice Exercises

  1. Build a complete CRUD API for a "tasks" resource: create, list, get one, update, delete.
  2. Add pagination to your list endpoint: /tasks?page=1&per_page=10
  3. Add API key authentication to your API using a decorator.
  4. Organize a medium-sized API using Flask Blueprints โ€” separate users, tasks, and auth into different files.
โ† Flask Basics