DEV Community

nk sk
nk sk

Posted on

๐Ÿš€ Building Powerful Mock Servers with FastAPI

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"}
Enter fullscreen mode Exit fullscreen mode

Run it:

uvicorn app:app --reload --port 8000
Enter fullscreen mode Exit fullscreen mode

๐Ÿ‘‰ 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"}
Enter fullscreen mode Exit fullscreen mode

โœ… 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)
Enter fullscreen mode Exit fullscreen mode

๐Ÿ‘‰ 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}
Enter fullscreen mode Exit fullscreen mode

โœ… 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"}
Enter fullscreen mode Exit fullscreen mode

โœ… 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
Enter fullscreen mode Exit fullscreen mode

Or orchestrate them with Docker Compose:

version: "3.8"
services:
  users:
    build: ./users_mock
    ports: ["8001:8000"]
  orders:
    build: ./orders_mock
    ports: ["8002:8000"]
Enter fullscreen mode Exit fullscreen mode

๐Ÿ”น 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"]
Enter fullscreen mode Exit fullscreen mode

Build & run:

docker build -t mock-api .
docker run -p 8000:8000 mock-api
Enter fullscreen mode Exit fullscreen mode

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()}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ”น 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)