How to Scaffold a Python FastAPI Service Using AI Prompt Architect
This guide shows you how to use AI Prompt Architect to generate a production-ready Python FastAPI microservice — with Pydantic validation, async database access, structured logging, and Docker deployment.
The Problem with Unstructured API Development
Starting a FastAPI project without a structured prompt leads to inconsistent patterns across your codebase:
- Mixed sync and async database calls
- Inconsistent error response formats
- No clear separation between routes, services, and data access
- Missing authentication middleware
- No standardised logging or observability
A Master Prompt prevents all of this by encoding your architectural decisions upfront.
Step 1: Configure Your Stack in the Wizard
# FastAPI project configuration
framework: FastAPI 0.115+
language: Python 3.12 (type hints required)
validation: Pydantic v2
database: SQLAlchemy 2.0 (async) + PostgreSQL
auth: JWT with python-jose
testing: pytest + httpx (async)
deployment: Docker + uvicorn
Step 2: Generate the Master Prompt
AI Prompt Architect produces a structured prompt that enforces your full architecture. Here's the folder structure it specifies:
## Folder Structure
app/
├── main.py # FastAPI application factory
├── config.py # Pydantic Settings for env vars
├── dependencies.py # Dependency injection (DB session, auth)
├── models/ # SQLAlchemy ORM models
│ ├── base.py # Declarative base + mixins
│ └── user.py
├── schemas/ # Pydantic request/response schemas
│ └── user.py
├── routers/ # API route handlers
│ └── users.py
├── services/ # Business logic layer
│ └── user_service.py
├── middleware/ # Custom middleware (CORS, logging, auth)
│ └── auth.py
└── tests/
├── conftest.py # Fixtures: async client, test DB
└── test_users.py
Step 3: Scaffold with Your AI Assistant
Paste the Master Prompt and ask your AI to generate specific components. The AI follows the exact layered architecture you defined:
# app/routers/users.py — Generated by AI with Master Prompt
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from app.dependencies import get_db, get_current_user
from app.schemas.user import UserCreate, UserResponse, UserUpdate
from app.services.user_service import UserService
from app.models.user import User
router = APIRouter(prefix="/users", tags=["users"])
@router.post("/", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
async def create_user(
payload: UserCreate,
db: AsyncSession = Depends(get_db),
) -> UserResponse:
"""Create a new user account."""
service = UserService(db)
user = await service.create(payload)
return UserResponse.model_validate(user)
@router.get("/me", response_model=UserResponse)
async def get_current_user_profile(
current_user: User = Depends(get_current_user),
) -> UserResponse:
"""Return the authenticated user's profile."""
return UserResponse.model_validate(current_user)
@router.patch("/me", response_model=UserResponse)
async def update_profile(
payload: UserUpdate,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
) -> UserResponse:
"""Update the authenticated user's profile."""
service = UserService(db)
updated = await service.update(current_user.id, payload)
return UserResponse.model_validate(updated)
Notice how every route follows the same pattern: dependency injection for DB and auth, Pydantic schemas for I/O, service layer for business logic. This consistency comes from the Master Prompt.
Step 4: Generate the Service Layer
The AI generates a clean service layer that separates business logic from HTTP handling:
# app/services/user_service.py
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from fastapi import HTTPException, status
from app.models.user import User
from app.schemas.user import UserCreate, UserUpdate
class UserService:
def __init__(self, db: AsyncSession):
self.db = db
async def create(self, data: UserCreate) -> User:
existing = await self.db.execute(
select(User).where(User.email == data.email)
)
if existing.scalar_one_or_none():
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="Email already registered",
)
user = User(**data.model_dump())
self.db.add(user)
await self.db.commit()
await self.db.refresh(user)
return user
async def update(self, user_id: str, data: UserUpdate) -> User:
user = await self.db.get(User, user_id)
if not user:
raise HTTPException(status_code=404, detail="User not found")
for field, value in data.model_dump(exclude_unset=True).items():
setattr(user, field, value)
await self.db.commit()
await self.db.refresh(user)
return user
Step 5: Refine and Add Infrastructure
Use the Refine feature to add missing infrastructure concerns:
- Structured JSON logging with
structlog - Health check endpoint at
/healthz - Dockerfile with multi-stage build
- Alembic migration configuration
pytestfixtures with async test client
Key Takeaways
- Layered architecture from day one — routers, services, and models stay cleanly separated
- Type safety throughout — Pydantic v2 schemas enforce input/output contracts
- Async by default — the Master Prompt ensures all DB access is async
- Production-ready patterns — dependency injection, error handling, and testing are baked in
Ready to build better prompts?
Start using AI Prompt Architect for free today.
Get Started Free