Fastcrud
benavlabs/fastcrud
A skill your agent uses when building or modifying CRUD endpoints with FastCRUD (the fastcrud PyPI package) in a FastAPI project — covers FastCRUD, crudrouter, EndpointCreator, FilterConfig…
A skill your agent uses when building, reviewing, testing, securing or shipping a FastAPI / async Python service — routers, Pydantic v2 schemas, dependency injection, async SQLAlchemy 2.0…
$ npx skills add ericrisco/rsc-harness --skill fastapi -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install ericrisco/rsc-harness fastapi --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/ericrisco/rsc-harness.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/fastapi .claude/skills/fastapi && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "fastapi" agent skill from https://github.com/ericrisco/rsc-harness/tree/main/skills/fastapi into .claude/skills/fastapi/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "fastapi", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/ericrisco/rsc-harness/tree/main/skills/fastapiType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add ericrisco/rsc-harness --skill fastapi -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install ericrisco/rsc-harness fastapi --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/ericrisco/rsc-harness.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/fastapi .agents/skills/fastapi && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "fastapi" agent skill from https://github.com/ericrisco/rsc-harness/tree/main/skills/fastapi into .agents/skills/fastapi/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "fastapi", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add ericrisco/rsc-harness --skill fastapi -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install ericrisco/rsc-harness fastapi --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/ericrisco/rsc-harness.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/fastapi .cursor/skills/fastapi && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "fastapi" agent skill from https://github.com/ericrisco/rsc-harness/tree/main/skills/fastapi into .cursor/skills/fastapi/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "fastapi", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/ericrisco/rsc-harness.git --path skills/fastapi--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add ericrisco/rsc-harness --skill fastapi -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install ericrisco/rsc-harness fastapi --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/ericrisco/rsc-harness.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/fastapi .gemini/skills/fastapi && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "fastapi" agent skill from https://github.com/ericrisco/rsc-harness/tree/main/skills/fastapi into .gemini/skills/fastapi/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "fastapi", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install ericrisco/rsc-harness fastapiInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add ericrisco/rsc-harness --skill fastapi -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/ericrisco/rsc-harness.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/fastapi .github/skills/fastapi && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "fastapi" agent skill from https://github.com/ericrisco/rsc-harness/tree/main/skills/fastapi into .github/skills/fastapi/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "fastapi", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add ericrisco/rsc-harness --skill fastapi -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install ericrisco/rsc-harness fastapi --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/ericrisco/rsc-harness.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/fastapi .opencode/skills/fastapi && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "fastapi" agent skill from https://github.com/ericrisco/rsc-harness/tree/main/skills/fastapi into .opencode/skills/fastapi/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "fastapi", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
fastapiA skill your agent uses when building, reviewing, testing, securing or shipping a FastAPI / async Python service — routers, Pydantic v2 schemas, dependency injection, async SQLAlchemy 2.0…
Fastapi is an agent skill from ericrisco/rsc-harness. Use when building, reviewing, testing, securing or shipping a FastAPI / async Python service — routers, Pydantic v2 schemas, dependency injection, async SQLAlchemy 2.0, OAuth2/JWT, ASGITransport tests, production wiring. NOT language-level Python or packaging (that is python), NOT engine-level SQL (that is postgresdb), NOT framework-agnostic REST contracts (that is api-design).
Its SKILL.md is about 5k tokens, which your agent loads only when the skill is triggered. The skill folder holds 10 other files, including scripts and reference files (for example `evals/README.md`, `evals/cases.yaml` and `references/database.md`).
It sits in Backend & APIs, covering Backend development, API design and ORMs and data access. It works with FastAPI, Python, Pydantic and SQL. The repository describes itself as: Your agent invents things because it has no memory, and can't touch your database because it has no arms. rsc is the meta-harness that gives it both, plus the trade to know the… The licence is MIT.
8 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit e3d5b33. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Ships 1 file in scripts/ (Shell), which the agent can run.
Shell commands in SKILL.md call:
mypypytestFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Fastapi loads about 5k tokens when it runs, and up to ~17k if it reads all its reference files. Until then it costs about 99 tokens; SKILL.md has 1,051 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check noted patterns worth knowing about, such as sudo or a known installer.
el_config = SettingsConfigDict(env_file=".env", env_prefix="APP_", extra="ignore")ignore[call-arg] # values come from env/.envAutomated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); the scripts in this folder are not scanned.
The full file from ericrisco/rsc-harness at commit e3d5b33, republished under its MIT licence (© ericrisco). 1,051 words, ~4,957 tokens.
.claude/skills/fastapi/SKILL.md (or your agent's skills folder). This skill also uses 7 other files; get the full folder from GitHub.The single authoritative skill for building, reviewing, testing, securing and shipping an async FastAPI service on Python 3.12+. The mental model: the app is a thin async HTTP layer over typed dependencies, a service/repository core, and explicit DB sessions. Routes validate and delegate; they never own business logic, raw SQL, or secrets.
Pinned stack: Python 3.12+, FastAPI 0.136+, Starlette 1.0+ (FastAPI 0.136 requires it;
avoid <1.0.1, GHSA-86qp-5c8j-p5mr), Pydantic v2 (2.7+) + pydantic-settings 2.x,
SQLAlchemy 2.0 async, Alembic 1.13+, asyncpg 0.30 / psycopg 3, httpx 0.28+,
pytest 8 + pytest-asyncio 1.0+ (asyncio_mode=auto), ruff 0.7+, mypy 1.13+ strict,
uv 0.5+, uvicorn 0.32+ / gunicorn 23+ + uvicorn-worker 0.3+, PyJWT 2.10+, argon2-cffi 23+,
pip-audit 2.7+, PostgreSQL 16. (All lower bounds; install the latest in each line.)
⚠️ SDD new-feature gate — read this first. If this skill fired on a new, non-trivial feature or behaviour change and there is no approved spec + plan under
02-DOCS/wiki/sdd/, STOP — do not write feature code yet. Hand off to../specify/SKILL.mdfirst: it runs brainstorm → spec → plan → tasks before any code, then routes back here once the plan is approved. Build here directly only for a genuinely one-line / low-risk change. Method:../sdd/SKILL.md.
Out of scope, and where it goes instead: django for Django; Flask / sync WSGI,
notebooks and CLI-only scripts (no skill); language-level Python, typing and packaging →
python; framework-agnostic REST contracts — status codes, URL naming, versioning,
cursor vs offset → api-design (this skill covers their FastAPI implementation);
engine-level schema, indexing, EXPLAIN, zero-downtime migrations, PgBouncer →
postgresdb; language-agnostic injection / secret / authz theory →
secure-coding; Dockerfile, Compose and CI/CD mechanics →
deployment (this skill keeps only a Docker note).
async def for any I/O route + async drivers (asyncpg, httpx); never requests/psycopg2/blocking calls on the loop (offload via await anyio.to_thread.run_sync).XCreate/XUpdate/XResponse (from_attributes=True); responses never leak hashes/tokens/internal flags.Annotated[T, Depends(...)], never built inline — so tests can override them.get_db (commit-on-success / rollback-on-exception); handlers never commit.{"error":{"code","message","details?"}} via centralized handlers; never leak stack traces / SQL.pydantic-settings (BaseSettings), never scattered os.getenv.exp/iss/aud and pin algorithms=["RS256"|"HS256"] explicitly.ASGITransport + dependency_overrides on a transactional DB; CI gates on ruff, mypy --strict, pytest --cov, pip-audit.app/
├── main.py # create_app() factory + lifespan; app = create_app()
├── core/
│ ├── config.py # Settings(BaseSettings) + get_settings()
│ ├── security.py # hashing, JWT encode/decode
│ └── logging.py # structlog / JSON logging setup
├── api/
│ ├── deps.py # get_db, get_current_user, Pagination, require_roles
│ └── routers/
│ ├── users.py
│ └── health.py
├── schemas/ # Pydantic v2 models (Create/Update/Response)
│ └── user.py
├── models/ # SQLAlchemy 2.0 DeclarativeBase models
│ └── user.py
├── db/
│ ├── base.py # engine, async_sessionmaker, Base
│ └── repository.py # generic async Repository[ModelT]
├── services/ # business logic (no FastAPI imports)
│ └── user_service.py
├── exceptions.py # AppError hierarchy + register_exception_handlers
tests/ # pytest-asyncio + ASGITransport
alembic/ # async env.py + versions/
pyproject.toml # ruff + mypy strict + pytest configRouters stay thin, services hold the logic, the repository/CRUD layer owns persistence.
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.api.routers import health, users
from app.core.config import Settings, get_settings
from app.db.base import engine
from app.exceptions import register_exception_handlers
# Routers paired with their mount prefix + OpenAPI tag, declared once so the factory
# stays a flat loop instead of a wall of include_router() calls.
ROUTERS = (
(health.router, "/health", "health"),
(users.router, "/api/v1/users", "users"),
)
@asynccontextmanager
async def lifespan(_app: FastAPI):
# Open pools/caches on startup (here), never at import time, so importing the module has
# no side effects (tests and Alembic import it freely).
yield
await engine.dispose() # release pooled DB connections so workers exit cleanly
def _install_cors(app: FastAPI, settings: Settings) -> None:
if not settings.cors_origins:
return # no browser clients configured -> skip the middleware entirely
app.add_middleware(
CORSMiddleware,
allow_origins=settings.cors_origins, # explicit per-env list, never ["*"] with creds
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "PATCH", "DELETE"],
allow_headers=["Authorization", "Content-Type"],
)
def create_app(settings: Settings | None = None) -> FastAPI:
settings = settings or get_settings()
app = FastAPI(title=settings.api_title, version=settings.api_version, lifespan=lifespan)
register_exception_handlers(app)
_install_cors(app, settings)
for router, prefix, tag in ROUTERS:
app.include_router(router, prefix=prefix, tags=[tag])
return app
app = create_app()Accepting an optional settings argument lets tests build the app with overridden config
without touching the get_settings cache. Bad = allow_origins=["*"] with
allow_credentials=True — browsers reject it and Starlette refuses to echo * for
credentialed requests. → references/production.md for proxy headers / logging wiring at
startup.
To inject servers / security schemes / a logo into the generated OpenAPI doc, assign a
custom builder to app.openapi inside create_app(). → references/production.md
(Customizing the OpenAPI schema).
from functools import lru_cache
from pydantic import PostgresDsn, SecretStr
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", env_prefix="APP_", extra="ignore")
api_title: str = "Service API"
api_version: str = "1.0.0"
environment: str = "development"
database_url: PostgresDsn
jwt_secret: SecretStr
jwt_algorithm: str = "HS256"
jwt_issuer: str = "service-api"
jwt_audience: str = "service-clients"
access_token_ttl_seconds: int = 900
cors_origins: list[str] = []
@lru_cache
def get_settings() -> Settings:
return Settings() # type: ignore[call-arg] # values come from env/.envBad = DB_URL = os.environ["DB_URL"] at import time (crashes on import, untyped,
unmockable). Good = inject get_settings as a dependency so tests override it.
from datetime import datetime
from typing import Annotated
from uuid import UUID
from pydantic import BaseModel, ConfigDict, EmailStr, Field, computed_field
# Reusable constrained types keep the same rule in one place across the three models.
FullName = Annotated[str, Field(min_length=1, max_length=100)]
RawPassword = Annotated[str, Field(min_length=12, max_length=128)]
class UserInput(BaseModel):
"""Fields a client may send. Create/Update narrow this; Response never inherits it."""
email: EmailStr
full_name: FullName
class UserCreate(UserInput):
password: RawPassword
class UserUpdate(BaseModel):
# Every field optional: a PATCH sends only what changes.
email: EmailStr | None = None
full_name: FullName | None = None
class UserResponse(BaseModel):
model_config = ConfigDict(from_attributes=True) # populate straight off ORM attributes
id: UUID
email: EmailStr
full_name: str
created_at: datetime
@computed_field # type: ignore[prop-decorator]
@property
def label(self) -> str:
return f"{self.full_name} <{self.email}>"v2 migration cheats: use .model_dump() not .dict(); .model_validate(obj) not
.from_orm(); model_config = ConfigDict(...) not class Config;
field_validator/model_validator not @validator/@root_validator.
Bad = a response model with hashed_password: str (leaks the hash). Good = the
UserResponse above (no secret fields). → references/security.md.
from collections.abc import AsyncIterator
from dataclasses import dataclass
from typing import Annotated
from fastapi import Depends, Query
from sqlalchemy.ext.asyncio import AsyncSession
from app.db.base import async_session_factory
async def get_db() -> AsyncIterator[AsyncSession]:
session = async_session_factory()
try:
yield session
await session.commit() # commit only if the handler returned without raising
except Exception:
await session.rollback() # any error (incl. HTTP exceptions) unwinds the txn
raise
finally:
await session.close() # always release the connection back to the pool
DbSession = Annotated[AsyncSession, Depends(get_db)]
@dataclass(frozen=True)
class Pagination:
limit: int
offset: int
def get_pagination(
limit: Annotated[int, Query(ge=1, le=100)] = 50,
offset: Annotated[int, Query(ge=0)] = 0,
) -> Pagination:
return Pagination(limit=limit, offset=offset)
PageParams = Annotated[Pagination, Depends(get_pagination)]→ references/database.md for async_session_factory wiring; → references/security.md
for get_current_user and require_roles.
from fastapi import APIRouter, Response, status
from app.api.deps import CurrentUser, DbSession, PageParams
from app.schemas.user import UserCreate, UserResponse
from app.services import user_service
router = APIRouter()
@router.get("", response_model=list[UserResponse])
async def list_users(db: DbSession, page: PageParams) -> list[UserResponse]:
return await user_service.list_users(db, limit=page.limit, offset=page.offset)
@router.post("", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
async def create_user(payload: UserCreate, db: DbSession, response: Response) -> UserResponse:
user = await user_service.create_user(db, payload)
response.headers["Location"] = f"/api/v1/users/{user.id}"
return userBad = hashing the password + building select() + business rules inline in the route.
Good = await user_service.create_user(db, payload) (route stays thin). CurrentUser
is defined in references/security.md.
from fastapi import FastAPI, Request, status
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from app.core.logging import logger
class AppError(Exception):
def __init__(self, message: str, code: str, status_code: int = 500,
details: list[dict] | None = None) -> None:
super().__init__(message)
self.message = message
self.code = code
self.status_code = status_code
self.details = details or []
class NotFoundError(AppError):
def __init__(self, resource: str, ident: str) -> None:
super().__init__(f"{resource} not found: {ident}", "not_found", 404)
def register_exception_handlers(app: FastAPI) -> None:
@app.exception_handler(AppError)
async def _app_error(request: Request, exc: AppError) -> JSONResponse:
return JSONResponse(
status_code=exc.status_code,
content={"error": {"code": exc.code, "message": exc.message, "details": exc.details}},
)
@app.exception_handler(RequestValidationError)
async def _validation(request: Request, exc: RequestValidationError) -> JSONResponse:
details = [{"field": ".".join(map(str, e["loc"][1:])), "message": e["msg"], "code": e["type"]}
for e in exc.errors()]
return JSONResponse(
status_code=422,
content={"error": {"code": "validation_error", "message": "Request validation failed",
"details": details}},
)
@app.exception_handler(Exception)
async def _unhandled(request: Request, exc: Exception) -> JSONResponse:
logger.exception("unhandled_error", path=request.url.path)
return JSONResponse(
status_code=500,
content={"error": {"code": "internal_error", "message": "An unexpected error occurred"}},
)Keep this envelope identical across every handler — one code/message/details shape so
clients parse errors once. Subclass AppError per failure (each fixes a code + status):
NotFoundError (404), ConflictError (409), Unauthorized (401), Forbidden (403) — full
hierarchy in → references/production.md (AppError subclasses).
secure-coding has the why: error responses must never leak internals
(stack traces, SQL, secrets).
from datetime import datetime
from uuid import UUID, uuid4
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from app.core.config import get_settings
engine = create_async_engine(str(get_settings().database_url), pool_pre_ping=True)
async_session_factory = async_sessionmaker(engine, expire_on_commit=False)
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "users"
id: Mapped[UUID] = mapped_column(primary_key=True, default=uuid4)
email: Mapped[str] = mapped_column(unique=True, index=True)
full_name: Mapped[str]
hashed_password: Mapped[str]
created_at: Mapped[datetime] = mapped_column(server_default=func.now())
async def list_users(db: AsyncSession, limit: int, offset: int) -> list[User]:
result = await db.execute(
select(User).order_by(User.created_at.desc()).limit(limit).offset(offset)
)
return list(result.scalars().all())Fetch by PK with await db.get(Model, pk), by unique key with .scalar_one_or_none(); eager-load with
selectinload (collections) / joinedload (many-to-one).
→ references/database.md for relationships, N+1 / eager loading, repository, Alembic, pooling.
from fastapi import BackgroundTasks
# Good: in-request, non-durable side effect (best-effort email).
@router.post("/signup")
async def signup(background: BackgroundTasks) -> dict[str, str]:
background.add_task(send_welcome_email, "user@example.com")
return {"status": "accepted"}Anything needing retries, durability, or cross-process execution (payment webhooks, large
jobs) goes to a real broker (Celery / Arq / Dramatiq), never BackgroundTasks — it runs
in-process and dies with the worker, with no retry or visibility.
Drive the app in-process with httpx.AsyncClient(transport=ASGITransport(app=create_app()))
and swap real dependencies via app.dependency_overrides[get_db] = lambda: db_session against
a transactional fixture — so every test rolls back. Use pytest-asyncio with
asyncio_mode = "auto" (no @pytest.mark.asyncio), and assert secrets never serialize (e.g.
assert "hashed_password" not in resp.json()). TDD red→green→refactor. Full fixtures
(transactional begin_nested, auth overrides, respx, coverage gate) in
→ references/testing.md.
Full hardening playbook — argon2 hashing, OAuth2 + JWT (claims validated, algorithms
pinned), get_current_user/require_roles RBAC, CORS, shared-store rate limiting, injection,
SecretStr + log redaction, security headers, pip-audit — lives in
→ references/security.md — including the exact argon2.PasswordHasher().hash(pw) and
jwt.decode(t, key, algorithms=[...], audience=..., issuer=...) calls. See
secure-coding for the language-agnostic theory.
ASGI/worker math, structured JSON logging + request-id, liveness vs readiness probes, graceful
shutdown, keyset pagination, caching, ORJSONResponse and proxy headers all live in
→ references/production.md. See deployment for the
Dockerfile and CI/CD pipeline.
| Anti-pattern | Why it breaks → do this |
|---|---|
requests (or any blocking call) inside an async def route | Blocks the event loop → use httpx.AsyncClient. |
.dict() / from_orm() on a model | Pydantic v2: use .model_dump(); .dict()/from_orm are deprecated. |
Returning the ORM object with no response_model | Leaks columns + lazy-loads in serializer → declare response_model. |
allow_origins=['*'] together with credentials | Browser rejects it; Starlette blocks it. Pin origins. |
Decoding a JWT without checking exp/aud | Forged/replayed tokens. Validate exp/iss/aud + pin alg. |
| Building the WHERE clause with an f-string | SQLi. Bound params / SQLAlchemy expressions only. |
| One global session shared by the whole app | Cross-request data bleed + concurrency bugs. One session per request. |
Catching Exception and returning its message | Leaks internals. Log it, return generic 500. |
BackgroundTasks for a payment-webhook retry | No durability/retry. Use a real broker. |
Skipping mypy --strict because it is noisy | Strict catches the bugs FastAPI's runtime won't. Keep it. |
| Committing inside the handler | Let get_db own commit/rollback; handlers stay thin. |
| Default-mutable arg / engine built at import time | Mutable defaults bite; engine must live in lifespan. |
In a project with a 02-DOCS/ layer (harness), read
02-DOCS/wiki/stack/fastapi.md first and stay consistent with it; create or update it with this
project's real choices — auth model (JWT/OAuth2 provider, token TTLs), DB session + migration tool,
error-envelope shape, settings/secrets approach, deployment target — bump its Updated date, and index
it in 02-DOCS/wiki/index.md. No 02-DOCS/? Skip silently. Conventions are recorded, not gated —
never block the task on this.
Verify gate: scripts/verify.sh.
© ericrisco, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 7 other files (scripts, references) in skills/fastapi of ericrisco/rsc-harness.
Open the folder on GitHubat commit e3d5b33
Fastapi next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Fastapi this skillericrisco/rsc-harness | 167 | — | ~5k | Automated safety check: Notes | MIT | |
| Fastcrudbenavlabs/fastcrud | 1.6k | — | ~5k | Automated safety check: Pass | MIT | |
| Fastapi Appccplugins/awesome-claude-code-plugins | 968 | — | ~1.1k | Automated safety check: Notes | Apache-2.0 | |
| Fastapi Prodavila7/claude-code-templates | 32k | 8 repos | ~1.6k | Automated safety check: Pass | MIT | |
| FastAPI ExpertJeffallan/claude-skills | 12k | — | ~1.8k | Automated safety check: Pass | MIT | |
| Backend Fastapi Pythonavibebuilder/claude-prime | 120 | — | ~997 | Automated safety check: Pass | MIT |
benavlabs/fastcrud
A skill your agent uses when building or modifying CRUD endpoints with FastCRUD (the fastcrud PyPI package) in a FastAPI project — covers FastCRUD, crudrouter, EndpointCreator, FilterConfig…
ccplugins/awesome-claude-code-plugins
Bootstrap a new FastAPI backend with async SQLAlchemy 2.0, asyncpg, Alembic, Pydantic v2, and no deprecated APIs.
davila7/claude-code-templates
Build high-performance async APIs with FastAPI, SQLAlchemy 2.0, and Pydantic V2.
Jeffallan/claude-skills
Builds async Python APIs with FastAPI and Pydantic V2, covering endpoints, JWT authentication, async SQLAlchemy, WebSockets and pytest checks against the OpenAPI docs.
avibebuilder/claude-prime
A skill your agent uses for any Python backend work in this project: building FastAPI endpoints, writing service functions, defining Pydantic/SQLModel schemas, running Alembic migrations, or…
aiskillstore/marketplace
Python FastAPI backend development with async patterns, SQLAlchemy, Pydantic, authentication, and production API patterns.
ericrisco/rsc-harness
A skill your agent uses when designing or analyzing a controlled experiment — falsifiable hypothesis, sample size from an MDE, reading significance/CI/power, CUPED, or rescuing tests that won't go…
ericrisco/rsc-harness
A skill your agent uses when making a web UI conform to WCAG 2.2 Level AA — axe-core or Lighthouse a11y violations, keyboard operability, focus management, ARIA roles/names/live regions, contrast…
ericrisco/rsc-harness
A skill your agent uses when running or fixing paid acquisition on Google or Meta — campaign structure (Performance Max, Demand Gen, Search, Advantage+), platform-fit creative, budget/scaling rules…
ericrisco/rsc-harness
A skill your agent uses when measuring whether an LLM or agent system actually got better and gating merges on it: golden sets, fixing an inflated LLM-as-judge, scoring RAG (faithfulness, contextual…
ericrisco/rsc-harness
A skill your agent uses when a creative goal must become a finished media file: pick and order generative-media models per modality — AI voiceover, image-to-video clips, score — then glue them with…
ericrisco/rsc-harness
A skill your agent uses when instrumenting product or web analytics — GA4/PostHog SDK wiring, event taxonomy, funnels, double-counted events, consent gating, PII scrubbing.
Categories
A skill your agent uses when building, reviewing, testing, securing or shipping a FastAPI / async Python service — routers, Pydantic v2 schemas, dependency injection, async SQLAlchemy 2.0…. Fastapi is an agent skill from ericrisco/rsc-harness.0, OAuth2/JWT, ASGITransport tests, production wiring.
Fastapi fits situations like: shipping a FastAPI / async Python service — routers; pydantic v2 schemas; dependency injection; async SQLAlchemy 2.0.
Run `npx skills add ericrisco/rsc-harness --skill fastapi -a claude-code`. Or copy the skill folder (skills/fastapi in ericrisco/rsc-harness) into .claude/skills/fastapi in your project. Claude Code loads it when a task matches its description.
Run `npx skills add ericrisco/rsc-harness --skill fastapi -a codex`. Or copy the skill folder (skills/fastapi in ericrisco/rsc-harness) into .agents/skills/fastapi in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add ericrisco/rsc-harness --skill fastapi -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/fastapi, .gemini/skills/fastapi, .github/skills/fastapi and .opencode/skills/fastapi in your project.
Going by SKILL.md and its folder, Fastapi needs a shell for the scripts in its folder and the command-line tools its instructions call (mypy and pytest). Our summary lists: Python 3; A Bash shell; Docker.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found notes only (mentions a .env file), nothing it rates as a warning. It is not a guarantee. The check reads SKILL.md only: the scripts in the folder are not scanned, so read them before running anything.
Fastapi is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 5k tokens (SKILL.md is roughly 20k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 12k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Fastapi: Fastcrud (benavlabs/fastcrud, 1.6k stars), Fastapi App (ccplugins/awesome-claude-code-plugins, 968 stars), Fastapi Pro (davila7/claude-code-templates, 32k stars) and FastAPI Expert (Jeffallan/claude-skills, 12k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
ericrisco (a GitHub user) maintains it in ericrisco/rsc-harness, which has 167 GitHub stars. The repository holds 227 skills in this directory. The repository was last updated on October 7, 2026.
Source: ericrisco/rsc-harness on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.