🐍 Python Q84 / 170

How to build REST API using Python?

AI-Powered Answer ✓ Answered

REST (Representational State Transfer) APIs are a standard way for different software systems to communicate over the internet. Python offers several powerful frameworks that simplify the process of creating robust and scalable RESTful services. This guide will walk you through the basics using popular choices like Flask and FastAPI.

Popular Python Frameworks for REST APIs

When it comes to building REST APIs in Python, two of the most popular and versatile frameworks are Flask and FastAPI. Django REST Framework (DRF) is also an excellent choice if you're already using Django for your web application and need a full-featured solution.

Example using Flask

Flask is a lightweight micro-framework, making it an excellent choice for smaller APIs or when you want more control over components. It's simple to get started with and highly flexible.

python
from flask import Flask, jsonify, request

app = Flask(__name__)

# In-memory data store for demonstration
items = [
    {"id": 1, "name": "Item 1", "description": "This is item 1"},
    {"id": 2, "name": "Item 2", "description": "This is item 2"}
]
next_id = 3

@app.route('/items', methods=['GET'])
def get_items():
    return jsonify(items)

@app.route('/items/<int:item_id>', methods=['GET'])
def get_item(item_id):
    item = next((item for item in items if item['id'] == item_id), None)
    if item:
        return jsonify(item)
    return jsonify({"message": "Item not found"}), 404

@app.route('/items', methods=['POST'])
def create_item():
    global next_id
    new_item = request.json
    if not new_item or 'name' not in new_item:
        return jsonify({"message": "Invalid item data"}), 400
    
    new_item['id'] = next_id
    items.append(new_item)
    next_id += 1
    return jsonify(new_item), 201

@app.route('/items/<int:item_id>', methods=['PUT'])
def update_item(item_id):
    item = next((item for item in items if item['id'] == item_id), None)
    if not item:
        return jsonify({"message": "Item not found"}), 404
    
    updated_data = request.json
    if not updated_data:
        return jsonify({"message": "No update data provided"}), 400
    
    item.update(updated_data)
    return jsonify(item)

@app.route('/items/<int:item_id>', methods=['DELETE'])
def delete_item(item_id):
    global items
    initial_len = len(items)
    items = [item for item in items if item['id'] != item_id]
    if len(items) < initial_len:
        return jsonify({"message": "Item deleted"}), 204
    return jsonify({"message": "Item not found"}), 404

if __name__ == '__main__':
    app.run(debug=True)

In this Flask example, we define routes for GET (all items and specific item), POST (create new item), PUT (update item), and DELETE (remove item). request.json is used to parse incoming JSON data, and jsonify converts Python dictionaries to JSON responses. To run this, save it as app.py and execute python app.py.

Example using FastAPI

FastAPI is a modern, fast (high-performance) web framework for building APIs with Python 3.7+ based on standard Python type hints. It automatically generates interactive API documentation (Swagger UI, ReDoc).

python
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List, Dict, Optional

app = FastAPI()

# Pydantic model for data validation and serialization
class Item(BaseModel):
    id: Optional[int] = None # Optional for POST requests, will be set by server
    name: str
    description: Optional[str] = None
    price: float = 0.0

# In-memory data store
items_db: Dict[int, Item] = {
    1: Item(id=1, name="Laptop", description="High-performance laptop", price=1200.0),
    2: Item(id=2, name="Mouse", description="Wireless ergonomic mouse", price=25.0)
}
next_item_id = 3

@app.get("/items", response_model=List[Item])
async def get_items():
    return list(items_db.values())

@app.get("/items/{item_id}", response_model=Item)
async def get_item(item_id: int):
    if item_id not in items_db:
        raise HTTPException(status_code=404, detail="Item not found")
    return items_db[item_id]

@app.post("/items", response_model=Item, status_code=201)
async def create_item(item: Item):
    global next_item_id
    item.id = next_item_id
    items_db[next_item_id] = item
    next_item_id += 1
    return item

@app.put("/items/{item_id}", response_model=Item)
async def update_item(item_id: int, item: Item):
    if item_id not in items_db:
        raise HTTPException(status_code=404, detail="Item not found")
    
    # Ensure the ID from path matches the ID in body if provided, or set it
    if item.id is not None and item.id != item_id:
        raise HTTPException(status_code=400, detail="Item ID in path and body do not match")

    item.id = item_id # Ensure the ID is correct for the update
    items_db[item_id] = item
    return item

@app.delete("/items/{item_id}", status_code=204)
async def delete_item(item_id: int):
    if item_id not in items_db:
        raise HTTPException(status_code=404, detail="Item not found")
    del items_db[item_id]
    return # FastAPI will return an empty 204 response automatically

FastAPI leverages Pydantic for data validation and serialization, and type hints for robust code and automatic documentation. It's built on Starlette for the web parts and Uvicorn for the server. To run this, install pip install "fastapi[all]" then save it as main.py and execute uvicorn main:app --reload.

Key Concepts for Building REST APIs

  • HTTP Methods: Use GET for retrieving data, POST for creating, PUT for full updates, PATCH for partial updates, and DELETE for removing resources.
  • Resource Naming: Use clear, plural nouns for endpoints (e.g., /users, /products/{id}). Avoid verbs in URIs to keep them consistent and intuitive.
  • Status Codes: Return appropriate HTTP status codes (e.g., 200 OK, 201 Created, 400 Bad Request, 404 Not Found, 500 Internal Server Error) to indicate the outcome of an API request.
  • Request/Response Bodies: Typically use JSON for exchanging data. Ensure proper serialization of Python objects to JSON and deserialization of incoming JSON to Python objects.
  • Authentication & Authorization: Secure your API using methods like API Keys, OAuth2, JWT (JSON Web Tokens), or session-based authentication to control access.
  • Error Handling: Provide clear, consistent, and descriptive error messages in your API responses, usually in JSON format, to help clients understand what went wrong.
  • Data Validation: Validate incoming request data to ensure it meets expected formats, types, and constraints, preventing malformed or malicious data from reaching your backend logic or database.

Deployment Considerations

  • WSGI/ASGI Servers: For Flask (which is WSGI-compliant), use production-grade WSGI servers like Gunicorn or uWSGI. For FastAPI (which is ASGI-compliant), use ASGI servers like Uvicorn.
  • Reverse Proxies: Place a reverse proxy like Nginx or Apache in front of your application server. This handles tasks such as load balancing, SSL termination, caching, and serving static files.
  • Containerization: Use Docker to package your application and its dependencies into a consistent, portable environment. This simplifies deployment and ensures your API runs the same way across different environments.
  • Cloud Platforms: Deploy your API on cloud providers like AWS (using services like EC2, Lambda, ECS/EKS), Google Cloud (Compute Engine, Cloud Run, GKE), Azure (App Service, AKS), or managed platforms like Heroku.