Advertisement
📡 Projects

Python API Project – Quotes REST API

Build a production-ready Quotes REST API using FastAPI and SQLite. Full CRUD, filtering, random endpoint, and Swagger docs in under 100 lines.

⏱️ 20 min read🎯 Projects📅 Updated 2026

Setup

Bash
pip install fastapi uvicorn sqlalchemy
uvicorn main:app --reload

Complete Implementation

Python
from fastapi import FastAPI, HTTPException, Query
from pydantic import BaseModel
from sqlalchemy import create_engine, Column, Integer, String, text
from sqlalchemy.orm import DeclarativeBase, Session
import random

engine = create_engine("sqlite:///quotes.db")

class Base(DeclarativeBase): pass

class QuoteModel(Base):
    __tablename__ = "quotes"
    id = Column(Integer, primary_key=True, index=True)
    text = Column(String, nullable=False)
    author = Column(String, nullable=False)
    category = Column(String, default="general")

Base.metadata.create_all(engine)

class QuoteCreate(BaseModel):
    text: str
    author: str
    category: str = "general"

class QuoteResponse(QuoteCreate):
    id: int
    class Config: from_attributes = True

app = FastAPI(title="Quotes API")

@app.get("/quotes", response_model=list[QuoteResponse])
def list_quotes(category: str | None = Query(None), skip: int = 0, limit: int = 20):
    with Session(engine) as db:
        q = db.query(QuoteModel)
        if category: q = q.filter(QuoteModel.category == category)
        return q.offset(skip).limit(limit).all()

@app.get("/quotes/random", response_model=QuoteResponse)
def random_quote(category: str | None = None):
    with Session(engine) as db:
        q = db.query(QuoteModel)
        if category: q = q.filter(QuoteModel.category == category)
        quotes = q.all()
        if not quotes: raise HTTPException(404, "No quotes")
        return random.choice(quotes)

@app.get("/quotes/{quote_id}", response_model=QuoteResponse)
def get_quote(quote_id: int):
    with Session(engine) as db:
        q = db.get(QuoteModel, quote_id)
        if not q: raise HTTPException(404, "Not found")
        return q

@app.post("/quotes", response_model=QuoteResponse, status_code=201)
def create_quote(quote: QuoteCreate):
    with Session(engine) as db:
        q = QuoteModel(**quote.dict())
        db.add(q); db.commit(); db.refresh(q)
        return q

@app.delete("/quotes/{quote_id}", status_code=204)
def delete_quote(quote_id: int):
    with Session(engine) as db:
        q = db.get(QuoteModel, quote_id)
        if not q: raise HTTPException(404, "Not found")
        db.delete(q); db.commit()
Bash
# API docs: http://localhost:8000/docs
Tip: FastAPI auto-generates Swagger at /docs and ReDoc at /redoc — use them to test all endpoints without any client code.

🏋️ Practical Exercise

Extend the API project:

  1. Add a new endpoint that updates an existing resource (PUT/PATCH).
  2. Return a 404 with a clear message when a requested id does not exist.
  3. Add input validation so invalid payloads are rejected with a 422/400.
  4. Add a simple filter query parameter to the list endpoint.

🔥 Challenge Exercise

Take the API to production-readiness: persist data in a database instead of memory, add token-based authentication on write routes, paginate the list endpoint, and write automated tests with TestClient covering success and error paths. Bonus: containerize the app with a Dockerfile and document the endpoints.

📋 Summary

  • This project builds a working REST API with create, read, update, and delete endpoints.
  • CRUD maps to HTTP methods with appropriate status codes (200/201/404/400).
  • Request bodies are validated with schemas (e.g. Pydantic) before processing.
  • Authentication protects write operations.
  • Automated tests verify both happy paths and error cases.
  • Swapping in-memory storage for a database makes the API persistent and production-ready.

Interview Questions on Building a REST API

  • How do you structure a REST API project for maintainability?
  • How do you map CRUD operations to HTTP methods and status codes?
  • How do you validate request data in an API?
  • How do you add authentication to selected routes?
  • How do you test an API?
  • How do you handle errors and return consistent error responses?
  • How would you move from in-memory storage to a real database?

FAQ

Should I use FastAPI or Flask for this project? +

FastAPI is a great fit for a modern API: automatic validation, type hints, and built-in docs. Flask works well too and is very flexible. Either lets you complete this project; FastAPI just removes more boilerplate.

How do I move from in-memory data to a database? +

Replace the Python list/dict store with a database layer — SQLite via sqlite3 or SQLAlchemy to start. Define models, and update each endpoint to query and persist through the database instead of the in-memory structure.

How do I test API endpoints? +

Use the framework’s test client (FastAPI’s TestClient or Flask’s test_client) to send requests in your tests and assert on status codes and response bodies — no running server needed.

How should errors be returned? +

Return appropriate status codes with a consistent JSON error body (e.g. {"detail": "Not found"}). Frameworks provide helpers like FastAPI’s HTTPException to do this cleanly.