When your frontend or integration teams are blocked waiting for real APIs, a mock server can save the day. Instead of waiting weeks for backend endpoints, you can spin up a fully functional mock service in minutes using FastAPI.
In this guide, weโll build an extensive FastAPI mock server that supports:
- โ CRUD APIs with JSON responses
- โ File downloads & uploads
- โ Simulated delays & error codes
- โ Auto-generated Swagger/OpenAPI docs
- โ Running multiple mock services
- โ Deploying via Docker
Letโs dive in.
๐น Why FastAPI for Mock Servers?
Python offers several ways to spin up mock servers (http.server
, Flask, etc.), but FastAPI stands out because:
- Itโs modern & async-friendly (great for handling concurrent requests).
- Auto-generates interactive Swagger UI at
/docs
. - Provides data validation with Pydantic models.
- Feels close to production backends, making tests realistic.
๐น Step 1: Basic FastAPI Mock Server
Letโs start with a minimal server.
from fastapi import FastAPI
app = FastAPI()
@app.get("/ping")
def ping():
return {"message": "pong"}
Run it:
uvicorn app:app --reload --port 8000
๐ Visit: http://localhost:8000/ping
โ {"message": "pong"}
๐ Swagger UI: http://localhost:8000/docs
๐น Step 2: Adding CRUD Endpoints
Letโs mock a simple User API with GET
, POST
, PUT
, and DELETE
.
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List
app = FastAPI(title="Mock User API")
class User(BaseModel):
id: int
name: str
users: List[User] = [User(id=1, name="Alice")]
@app.get("/users", response_model=List[User])
def get_users():
return users
@app.get("/users/{user_id}", response_model=User)
def get_user(user_id: int):
for u in users:
if u.id == user_id:
return u
raise HTTPException(status_code=404, detail="User not found")
@app.post("/users", response_model=User, status_code=201)
def create_user(user: User):
users.append(user)
return user
@app.put("/users/{user_id}", response_model=User)
def update_user(user_id: int, updated: User):
for i, u in enumerate(users):
if u.id == user_id:
users[i] = updated
return updated
raise HTTPException(status_code=404, detail="User not found")
@app.delete("/users/{user_id}")
def delete_user(user_id: int):
global users
users = [u for u in users if u.id != user_id]
return {"message": "deleted"}
โ
Now you have a mock User API with full CRUD.
โ
Test it in Swagger UI (/docs
).
๐น Step 3: Serving Files (Downloads)
You may want to test file downloads (e.g., PDFs, images).
from fastapi.responses import FileResponse
import os
FILES_DIR = "files"
os.makedirs(FILES_DIR, exist_ok=True)
# create a sample file
with open(f"{FILES_DIR}/sample.txt", "w") as f:
f.write("This is a mock file")
@app.get("/files/{filename}")
def download_file(filename: str):
path = os.path.join(FILES_DIR, filename)
if not os.path.exists(path):
raise HTTPException(status_code=404, detail="File not found")
return FileResponse(path, media_type="application/octet-stream", filename=filename)
๐ Now you can hit: http://localhost:8000/files/sample.txt
to download the mock file.
๐น Step 4: Uploading Files
from fastapi import UploadFile, File
@app.post("/files/upload")
async def upload_file(file: UploadFile = File(...)):
file_path = os.path.join(FILES_DIR, file.filename)
with open(file_path, "wb") as f:
content = await file.read()
f.write(content)
return {"filename": file.filename}
โ You can now upload files via Swagger UI and later download them.
๐น Step 5: Adding Realism (Delays & Errors)
Mocks should not always return 200 OK instantly. You can simulate network latency and error codes.
import time, random
@app.get("/unstable")
def unstable_endpoint():
# Simulate random delay
delay = random.uniform(0.5, 2.0)
time.sleep(delay)
# Simulate random error
if random.random() < 0.3: # 30% chance of failure
raise HTTPException(status_code=500, detail="Random server error")
return {"message": f"Success after {delay:.2f}s"}
โ This helps frontend teams test retry logic and error handling.
๐น Step 6: Running Multiple Mock Services
If your system has microservices, you can run multiple FastAPI apps:
-
users_mock/app.py
โ/users/*
-
orders_mock/app.py
โ/orders/*
Run them on different ports:
uvicorn users_mock.app:app --port 8001
uvicorn orders_mock.app:app --port 8002
Or orchestrate them with Docker Compose:
version: "3.8"
services:
users:
build: ./users_mock
ports: ["8001:8000"]
orders:
build: ./orders_mock
ports: ["8002:8000"]
๐น Step 7: Auto-Generated API Docs
One of FastAPIโs biggest advantages:
-
/docs
โ Swagger UI (interactive testing) -
/redoc
โ Redoc API docs
You can share these URLs with frontend developers so they know exactly what to expect.
๐น Step 8: Deployment with Docker
Create a simple Dockerfile
:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
Build & run:
docker build -t mock-api .
docker run -p 8000:8000 mock-api
Now your mock API runs anywhere โ locally, in CI pipelines, or in the cloud.
๐น Step 9: Enhancements
You can push mocks further by:
- Faker library โ generate fake user names, addresses, emails.
-
Versioning APIs โ
/v1/users
,/v2/users
. - Environment configs โ allow switching between real & mock APIs.
- Database-backed mocks โ instead of in-memory, use SQLite/JSON files for persistence.
Example with Faker:
from faker import Faker
fake = Faker()
@app.get("/random-user")
def random_user():
return {"id": fake.random_int(), "name": fake.name()}
๐น Summary
With FastAPI, you can create a mock server that feels real:
- โ Serve static files & downloads
- โ Handle file uploads
- โ Provide full CRUD APIs
- โ Simulate delays, errors, and flaky endpoints
- โ Run multiple services with Docker
- โ Auto-generate API docs
By investing a little effort, your team can have a production-like environment before the backend even exists โ keeping frontend, QA, and DevOps moving fast.
๐ก Pro tip: Put your FastAPI mocks in a separate repo and integrate them into CI/CD pipelines. This way, your team can always test against a consistent, predictable API environment.
Top comments (0)