12 FastAPI Prompts for Endpoints, Pydantic Validation, and Async Background Tasks

12 FastAPI Prompts for Endpoints, Pydantic Validation, and Async Background Tasks

FastAPI is the framework of choice for modern Python APIs — native async, automatic OpenAPI docs, and validation powered by Pydantic. The framework itself is fast, but your development speed depends on how well you direct an AI assistant. Most developers hit the same wall: vague prompts produce toy code that can't be merged into a real project.

This guide is a field manual. I've collected the exact prompts I use in daily work — for endpoints, request models, async database access, background processing, and everything around them. Every prompt follows the same pattern: give the AI a role, context, constraints, and the expected result. Do that, and you get code that survives code review.

Why Prompt Crafting Matters for FastAPI

Let's test two versions of the same request.

Naive prompt Effective prompt
"Create an endpoint" "Create a GET endpoint for users that returns a paginated list, raises 404 when not found, and documents the response model."
"Make validation" "Add Pydantic v2 validation with EmailStr, custom field validators, and unified 422 responses."
"Add a background task" "Parse a CSV in BackgroundTasks, persist results, and send a webhook on completion."

The second column gives the AI a framing that matches review standards. Everything below is built the same way: role + context + constraints + acceptance criteria.

Prompt 1. Project Scaffold

Prompt:

Act as a senior FastAPI developer. Generate a production-ready structure for a FastAPI app: a router-based layout, config via pydantic-settings, an async SQLAlchemy session setup, and Alembic migrations. Include the complete file tree and the key file contents. Use Python 3.12 and type hints everywhere.

Result you can expect:

app/
├── main.py
├── core/
│   ├── config.py
│   └── database.py
├── models/
├── schemas/
├── api/
│   └── v1/
│       └── endpoints/
└── tests/

The structure is worth more than the code itself. It forces you to think in modules instead of a single main.py file that grows out of control by week two.

Prompt 2. Pydantic Models for Request and Response

Prompt:

Create Pydantic v2 models for a User resource: email as EmailStr, a password field with min_length=8 and a custom validator that rejects common passwords, a phone number with a regex pattern, and a computed field for the display name. Return the full code plus a short test with pytest.

from pydantic import BaseModel, EmailStr, Field, field_validator

class UserCreate(BaseModel):
    email: EmailStr
    password: str = Field(min_length=8, max_length=64)
    phone: str = Field(pattern=r"^\+?[1-9]\d{7,14}$")

    @field_validator("password")
    @classmethod
    def reject_weak_passwords(cls, value: str) -> str:
        if value.lower() in {"password", "12345678", "qwerty123"}:
            raise ValueError("password is too common")
        return value

class UserRead(BaseModel):
    id: int
    email: EmailStr
    display_name: str
    model_config = {"from_attributes": True}

Practical tip: Always write "Pydantic v2" in the prompt. Most training data still contains v1 code. The field_validator decorator, Field(pattern=...), and model_config are the v2 way.

Prompt 3. Async Endpoints with Proper I/O

Prompt:

Write an async GET endpoint for FastAPI that fetches a user by ID using a SQLAlchemy 2.0 async session. If the user does not exist, raise 404 with a detail message. Add response_model and type hints.

@app.get("/users/{user_id}", response_model=UserRead)
async def get_user(user_id: int, session: AsyncSession = Depends(get_db)):
    user = await session.get(User, user_id)
    if user is None:
        raise HTTPException(status_code=404, detail="User not found")
    return user

Practical tip: Use async def only for I/O-bound code. If your endpoint does CPU-heavy work, use a plain def — FastAPI will run it in the threadpool automatically. Tell the AI about this constraint explicitly, otherwise it makes everything async.

Prompt 4. Background Processing with BackgroundTasks

Prompt:

Add a background task to a FastAPI endpoint: when a user uploads a CSV, parse the file in the background using BackgroundTasks, persist each row to the database, and log the outcome. Show the endpoint and keep the worker function in a separate module.

from fastapi import BackgroundTasks, UploadFile
from uuid import uuid4

def process_csv(path: str) -> None:
    # heavy work is done here
    ...

@app.post("/upload")
async def upload(file: UploadFile, background: BackgroundTasks):
    tmp = f"/tmp/{uuid4()}.csv"
    with open(tmp, "wb") as f:
        f.write(await file.read())
    background.add_task(process_csv, tmp)
    return {"status": "accepted"}

Practical tip: BackgroundTasks is enough for small jobs. If processing must survive a server restart or scale across workers, ask for Celery or arq instead. A good prompt states this boundary.

Prompt 5. Dependency Injection

Prompt:

Implement a FastAPI dependency for the current user: parse a JWT from the Authorization header, fetch the user from the database, and raise 401 on missing or invalid token. Then use this dependency in three protected routes.

async def get_current_user(
    authorization: str = Header(...),
    session: AsyncSession = Depends(get_db),
):
    token = authorization.removeprefix("Bearer ")
    payload = jwt.decode(token, settings.secret, algorithms=["HS256"])
    user = await session.get(User, int(payload["sub"]))
    if user is None:
        raise HTTPException(status_code=401, detail="Invalid credentials")
    return user

Dependencies are one of FastAPI's strongest features. The prompt gives the AI a contract — parse, fetch, validate, return — and the implementation is then easy to review.

Prompt 6. Error Handling, Done Once

Prompt:

Build a unified error handling layer for FastAPI: a custom error code scheme, a handler for RequestValidationError that returns standardized errors, and a fallback 500 handler that hides internal details and returns a reference ID.

@app.exception_handler(RequestValidationError)
async def validation_handler(request, exc):
    return JSONResponse(
        status_code=422,
        content={"error": "validation_failed", "details": exc.errors()},
    )

Global error handling belongs at the app level. Do not repeat it in every route; ask for it once and it will be used everywhere.

Prompt 7. Reusable Pagination

Prompt:

Implement reusable pagination for FastAPI: page and size query parameters, a generic Page schema with items, total, page, size, and a dependency that validates the parameters.

class Page(BaseModel):
    items: list
    total: int
    page: int
    size: int

async def pagination_params(
    page: int = Query(1, ge=1),
    size: int = Query(20, ge=1, le=100),
) -> dict:
    return {"offset": (page - 1) * size, "limit": size}

This prompt works because it defines the contract once. Every endpoint that needs pagination then just injects pagination_params.

Prompt 8. File Upload Validation

Prompt:

Create an endpoint for file uploads: accept a multipart file, check the MIME type and maximum size in one place, store the file with a uuid name, and return the public URL. Include a test.

Validation rules like "max 5 MB" must live in a single module. The prompt should say "centralize the check", otherwise the AI will duplicate the logic across endpoints.

Prompt 9. Async SQLAlchemy Integration

Prompt:

Set up SQLAlchemy 2.0 in async mode: engine, async_sessionmaker, the get_db dependency, and a Project model with a foreign key to User. Include alembic env.py configured for an async engine.

engine = create_async_engine(settings.database_url, echo=False)
SessionLocal = async_sessionmaker(engine, expire_on_commit=False)

async def get_db():
    async with SessionLocal() as session:
        yield session

Async database setup is a common failure point in generated code. The prompt must explicitly mention "async mode" and "SQLAlchemy 2.0", otherwise you get sync code wrapped in decorators.

Prompt 10. WebSocket for Real-Time Features

Prompt:

Implement a WebSocket chat endpoint: a ConnectionManager class that stores active connections, a broadcast method, and a route that handles connect, receive, and disconnect.

class ConnectionManager:
    def __init__(self):
        self.active: list[WebSocket] = []

    async def connect(self, websocket: WebSocket):
        await websocket.accept()
        self.active.append(websocket)

    async def broadcast(self, message: str):
        for connection in self.active:
            await connection.send_text(message)

WebSocket code is hard to write from memory. This prompt offloads the boilerplate while leaving the business logic — what messages mean — in your hands.

Prompt 11. Tests

Prompt:

Write pytest tests for the REST API: use httpx AsyncClient with ASGITransport, replace the database dependency with fixtures, and cover 200, 404, and 422 scenarios. Provide conftest.py with reusable fixtures.

@pytest.fixture
async def client():
    transport = ASGITransport(app=app)
    async with AsyncClient(transport=transport, base_url="http://test") as ac:
        yield ac

The magic in this prompt is "replace the database dependency with fixtures". It tells the AI to use dependency_overrides instead of spinning up a real database in tests.

Prompt 12. OpenAPI and Documentation Metadata

Prompt:

Improve OpenAPI metadata for the app: add title and version, a summary and description for each router, tags, and example values in request bodies. Provide the exact metadata structures.

app = FastAPI(
    title="Project API",
    version="2.0.0",
    description="Production-ready API built with FastAPI",
)

The payoff: your /docs endpoint becomes documentation your team and API consumers actually want to read.

The Meta-Prompt: How to Iterate

If the generated result is wrong, do not dump the code and start over. Say one specific thing: "return a dict instead of a tuple", "use an async session", "add status_code=201", "split this into two files". Small feedback loops give better results than enormous re-prompts.

A repeatable loop that works every time:

  1. Context: "We use FastAPI, SQLAlchemy 2.0, Pydantic v2."
  2. Constraints: "No synchronous DB code. Type hints everywhere."
  3. Acceptance criteria: "All endpoints covered by tests."
  4. Expected format: "Return code and one markdown table with file paths."

Conclusion

A FastAPI engineer's real advantage is the ability to turn thought into production code quickly. Prompts are the lever. Use the twelve above, adapt them to your stack, and you will stop writing boilerplate by hand — not because the AI replaces you, but because it removes the part of the job that is pure repetition.

Start small: paste Prompt 1 into your assistant and compare the scaffold to your current layout. Then move through the list, one prompt per task. Before you know it, you will have your own library of prompts, fine-tuned to the way you build APIs.

Go ahead — take these prompts, inject your business logic, and ship that API. If even one of them saves you a review cycle, this article has done its job. And if you have a prompt that works better than any of mine, send it over — I'm always collecting.

← All posts

Comments