fastapi-scaffold

Scaffold a new FastAPI project with standard structure, Pydantic models, dependency injection, and optional MCP/OAuth integration. Use when starting a new FastAPI service or adding a new API module to an existing project.

13 stars

Best use case

fastapi-scaffold is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Scaffold a new FastAPI project with standard structure, Pydantic models, dependency injection, and optional MCP/OAuth integration. Use when starting a new FastAPI service or adding a new API module to an existing project.

Teams using fastapi-scaffold should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/fastapi-scaffold-wip/SKILL.md --create-dirs "https://raw.githubusercontent.com/tae0y/python-project-template/main/.claude/skills.nouse/fastapi-scaffold-wip/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/fastapi-scaffold-wip/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How fastapi-scaffold Compares

Feature / Agentfastapi-scaffoldStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Scaffold a new FastAPI project with standard structure, Pydantic models, dependency injection, and optional MCP/OAuth integration. Use when starting a new FastAPI service or adding a new API module to an existing project.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# FastAPI Scaffold

You are a FastAPI expert applying Python coding conventions and MCP server patterns. Your goal is to scaffold a production-ready FastAPI project structure that the user can extend immediately.

## Your Expertise

- **FastAPI**: Routing, dependency injection, lifespan, middleware, exception handlers
- **Pydantic v2**: Settings management, request/response models, validators
- **Python conventions**: PEP 8, type hints everywhere, PEP 257 docstrings, `uv` as package manager
- **MCP integration**: Mounting FastMCP as ASGI sub-application onto FastAPI
- **Auth**: OAuth2 Bearer token pattern with FastAPI `Security` / `Depends`
- **Async**: `async def` for all I/O-bound handlers, `httpx.AsyncClient` for outbound calls
- **Testing**: `pytest` + `httpx.AsyncClient` + `pytest-asyncio` for integration tests

---

## Step 0: Design Document First (MANDATORY)

**Before writing any code**, create `localdocs/plan.fastapi-<name>.md` with the following template. Do not proceed to Step 1 until this exists and the user has approved it.

```markdown
# Plan: FastAPI — <service name>

## Purpose
[One sentence: what does this service do?]

## Stage
[ ] SPIKE  [ ] MVP  [ ] PRODUCTION

## Endpoints (draft)
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET    | /health | None | Health check |
| ...    | ...     | ...  | ...          |

## Data Models
- Request: ...
- Response: ...

## External Dependencies
- ...

## Open Questions
- [ ] ...
```

Ask the user to fill in or confirm each section before continuing.

---

## Step 1: Project Structure

Generate this layout (adjust if adding to an existing repo):

```
<project>/
├── src/
│   └── <package>/
│       ├── __init__.py
│       ├── main.py          # FastAPI app factory + lifespan
│       ├── config.py        # Pydantic BaseSettings
│       ├── dependencies.py  # Shared Depends() factories
│       ├── routers/
│       │   ├── __init__.py
│       │   └── <domain>.py  # One file per domain
│       ├── models/
│       │   ├── __init__.py
│       │   └── <domain>.py  # Pydantic request/response models
│       └── services/
│           ├── __init__.py
│           └── <domain>.py  # Business logic, no HTTP concerns
├── tests/
│   ├── conftest.py
│   └── test_<domain>.py
├── pyproject.toml
└── .env.example
```

---

## Step 2: Core Files

### `config.py` — Settings (always first)

```python
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    """Application settings loaded from environment variables."""

    model_config = SettingsConfigDict(env_file=".env", extra="ignore")

    app_name: str = "my-service"
    debug: bool = False
    # Add domain-specific settings here


settings = Settings()
```

### `main.py` — App factory + lifespan

```python
from contextlib import asynccontextmanager
from typing import AsyncGenerator

from fastapi import FastAPI

from .config import settings
from .routers import health, <domain>


@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
    """Manage startup and shutdown resources."""
    # startup: initialise DB pools, HTTP clients, etc.
    yield
    # shutdown: close connections


def create_app() -> FastAPI:
    """Create and configure the FastAPI application."""
    app = FastAPI(
        title=settings.app_name,
        debug=settings.debug,
        lifespan=lifespan,
    )

    app.include_router(health.router)
    app.include_router(<domain>.router, prefix="/api/v1")

    return app


app = create_app()
```

### `dependencies.py` — Shared dependencies

```python
from typing import Annotated

from fastapi import Depends, HTTPException, Security, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer

security = HTTPBearer(auto_error=False)


async def require_auth(
    credentials: Annotated[
        HTTPAuthorizationCredentials | None,
        Security(security),
    ],
) -> str:
    """Validate Bearer token and return the token string.

    Raises:
        HTTPException: 401 if token is missing or invalid.
    """
    if credentials is None:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Missing authentication token",
        )
    # TODO: validate credentials.credentials against your auth provider
    return credentials.credentials
```

### `routers/health.py` — Health check (always include)

```python
from fastapi import APIRouter
from pydantic import BaseModel

router = APIRouter(tags=["health"])


class HealthResponse(BaseModel):
    """Health check response."""

    status: str


@router.get("/health", response_model=HealthResponse)
async def health_check() -> HealthResponse:
    """Return service health status."""
    return HealthResponse(status="ok")
```

### `models/<domain>.py` — Request/response models

```python
from pydantic import BaseModel, Field


class <Domain>Request(BaseModel):
    """Request body for <domain> operations."""

    field_name: str = Field(..., description="Description of the field")


class <Domain>Response(BaseModel):
    """Response schema for <domain> operations."""

    id: str
    field_name: str
```

### `routers/<domain>.py` — Domain router

```python
from typing import Annotated

from fastapi import APIRouter, Depends

from ..dependencies import require_auth
from ..models.<domain> import <Domain>Request, <Domain>Response
from ..services.<domain> import <Domain>Service

router = APIRouter(prefix="/<domain>", tags=["<domain>"])


@router.post("/", response_model=<Domain>Response)
async def create_<domain>(
    body: <Domain>Request,
    token: Annotated[str, Depends(require_auth)],
    service: Annotated[<Domain>Service, Depends()],
) -> <Domain>Response:
    """Create a new <domain> resource.

    Args:
        body: Validated request body.
        token: Authenticated Bearer token.
        service: Injected domain service.

    Returns:
        Created <domain> resource.
    """
    return await service.create(body)
```

---

## Step 3: MCP Integration (optional, ask first)

Only add if the user confirms they need MCP alongside the REST API.

```python
# main.py addition
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("my-service")


@mcp.tool()
async def my_tool(param: str) -> str:
    """Tool description — becomes the MCP tool description.

    Args:
        param: Description of parameter.

    Returns:
        Result description.
    """
    return f"result: {param}"


def create_app() -> FastAPI:
    app = FastAPI(title=settings.app_name, lifespan=lifespan)
    # ... routers ...
    app.mount("/mcp", mcp.streamable_http_app())
    return app
```

---

## Step 4: Tests

### `tests/conftest.py`

```python
import pytest
import pytest_asyncio
from httpx import ASGITransport, AsyncClient

from <package>.main import app


@pytest_asyncio.fixture
async def client() -> AsyncClient:
    """Async test client for the FastAPI app."""
    async with AsyncClient(
        transport=ASGITransport(app=app),
        base_url="http://test",
    ) as ac:
        yield ac
```

### `tests/test_health.py`

```python
import pytest
from httpx import AsyncClient


@pytest.mark.asyncio
async def test_health_check(client: AsyncClient) -> None:
    """Health endpoint returns 200 with status ok."""
    response = await client.get("/health")

    assert response.status_code == 200
    assert response.json() == {"status": "ok"}
```

---

## Step 5: `pyproject.toml` (uv)

```toml
[project]
name = "<project>"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
    "fastapi>=0.115",
    "uvicorn[standard]>=0.32",
    "pydantic>=2.9",
    "pydantic-settings>=2.6",
]

[project.optional-dependencies]
mcp = ["mcp>=1.6"]
auth = ["python-jose[cryptography]>=3.3"]

[tool.uv]
dev-dependencies = [
    "pytest>=8",
    "pytest-asyncio>=0.24",
    "httpx>=0.27",
    "ruff>=0.8",
    "pyright>=1.1",
]

[tool.pytest.ini_options]
asyncio_mode = "auto"

[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B", "SIM"]
```

---

## Python Convention Reminders

Apply `python-conventions` throughout:

- **Type hints on every function** — parameters and return types, no exceptions
- **Docstrings on every public function** — PEP 257 format, include `Args:` and `Returns:` sections
- **Line length ≤ 88** (ruff default; matches Black)
- **Imports**: stdlib → third-party → local, sorted by `ruff --select I`
- **No bare `except:`** — always catch specific exception types
- **No mutable default arguments** — use `None` + body assignment

---

## Checklist Before Handing Off

- [ ] `localdocs/plan.fastapi-<name>.md` created and approved
- [ ] All endpoints documented in plan before implementation
- [ ] `config.py` uses `pydantic-settings` (no raw `os.environ`)
- [ ] Every handler has full type hints and docstring
- [ ] `GET /health` exists and returns `{"status": "ok"}`
- [ ] `tests/conftest.py` uses `ASGITransport`, not a live server
- [ ] `uv run pytest` passes before handing off
- [ ] `uv run ruff check .` passes
- [ ] MCP integration only if explicitly requested

---

## Anti-Patterns to Avoid

- ❌ Implementing endpoints before the plan doc exists
- ❌ Putting business logic inside router handlers (use services layer)
- ❌ Using `os.environ` directly instead of `Settings`
- ❌ `async def` on CPU-bound handlers (use `def` + `run_in_executor`)
- ❌ Skipping type hints "for speed" — they drive Pydantic schema generation
- ❌ Adding MCP without confirming the user needs it

Related Skills

worklog

13
from tae0y/python-project-template

Update worklog files by moving tasks between todo/doing/done states. Use when recording task progress, starting new work, or marking tasks complete. Requires explicit arguments: worklog [done|doing|todo] [description].

template-proposal-review

13
from tae0y/python-project-template

Review proposal files in localdocs/localdocs/proposals/ and decide whether to apply them to the upstream python-project-template repository. Use when evaluating proposals generated by template-upstream, deciding which ones to accept or reject, and applying accepted ones to the template repo. Triggers on: "proposal 검토", "제안 검토", "template에 반영할지", "proposals 리뷰", "upstream 반영 결정".

template-downstream

13
from tae0y/python-project-template

Update .claude/, .mcp.json, and .pre-commit-config.yaml from the upstream python-project-template repository into the current project. Only files that exist upstream are updated — project-only files are never deleted. Use when pulling latest tooling, rules, hooks, or skills from the canonical template into this project. Triggers on: "템플릿 최신화", "template sync", "upstream 반영", "template 업데이트".

template-broadcast

13
from tae0y/python-project-template

Apply template-downstream to all registered downstream projects in bulk, one project at a time. Use when you want to push the latest .claude/, .mcp.json, or .pre-commit-config.yaml to every project at once. Triggers on: "일괄 배포", "전체 프로젝트에 내려보내기", "모든 프로젝트 업데이트", "broadcast template", "bulk sync".

tdd

13
from tae0y/python-project-template

Test-Driven Development workflow. Use for ALL code changes - features, bug fixes, refactoring. TDD is non-negotiable.

sparks-create

13
from tae0y/python-project-template

Apply the 13 thinking tools from "Sparks of Genius" (생각의 탄생) to reframe any problem through creative, cross-disciplinary lenses. Use when conventional analysis falls short, when you need a fresh creative angle, or when the problem spans multiple domains.

skill-creator

13
from tae0y/python-project-template

Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy.

rethink-unblock

13
from tae0y/python-project-template

Break out of a stuck approach by reframing a technical problem through structured thinking frameworks. Use when blocked, going in circles, or when a fresh perspective is needed on architecture, design, or debugging.

refactoring

13
from tae0y/python-project-template

Refactoring assessment and patterns. Use after tests pass (GREEN phase) to assess improvement opportunities.

python-mcp-expert

13
from tae0y/python-project-template

Expert assistant for developing Model Context Protocol (MCP) servers in Python. Use when creating, debugging, or optimizing MCP servers with FastMCP, tools, resources, and prompts.

python-conventions

13
from tae0y/python-project-template

Python coding conventions and guidelines including PEP 8, type hints, docstrings, and testing standards. Automatically applied when working with Python files.

prd

13
from tae0y/python-project-template

Generate high-quality Product Requirements Documents (PRDs) for software systems and AI-powered features. Includes executive summaries, user stories, technical specifications, and risk analysis.