关于 building-python-web-apps
Building Python Web Apps 面向独立开发者和小团队,指导使用 Python 构建中小型 SaaS 应用。它提供可重复采用的 FastAPI 技术栈、应用结构,以及认证、计费、后台任务和部署方案。
主要功能
- 01以 FastAPI 为核心的应用蓝图
- 02分层组织路由、服务与模型
- 03数据库、配置和认证模式
- 04Stripe 计费与 Webhook 指引
- 05Docker 和 Terraform 部署模式
适用场景
- 01启动小型 SaaS 应用
- 02选择应用结构与默认技术栈
- 03实现计费和 Webhook 处理
- 04规划部署与后台任务
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ wdm0006/ python-skills --skill building-python-web-apps安装后在 Claude Code 中输入/building-python-web-apps确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
AUTH.mdBACKGROUND_JOBS.mdDEPLOYMENT.mdFRONTEND.mdPAYMENTS.mdSTACK.md详细介绍
构建 Python Web 应用
一套主张明确、内置齐全的蓝图,助你独自或与小团队一起交付中小型 SaaS 应用。它倾向于采用成熟、广为人知且每次都以相同方式连接的组件,让新应用在几小时而非几周内实现“身份验证 + 计费 + 部署”。
默认技术栈
| 关注点 | 默认方案 |
|---|---|
| 框架 | FastAPI(异步),uvicorn[standard] |
| 打包 | uv + pyproject.toml + uv.lock,hatchling,src/ 布局 |
| 数据库 | Postgres 16 + asyncpg,SQLAlchemy 2.0 异步,Alembic |
| 配置 | pydantic-settings BaseSettings,带环境变量前缀,@lru_cache |
| 支付 | Stripe ≥15,一个签名验证 Webhook,幂等性表 |
| 身份验证 | 依赖项守卫 + bcrypt;令牌方案按应用选择(参见 AUTH.md) |
| 前端 | Jinja2 + Tailwind (CDN) + Alpine.js,或 解耦的 SPA |
| 后台任务 | 以 cron 作为工作进程(相同镜像,不同命令)——不使用 Celery |
| 可观测性 | Sentry + PostHog,未配置密钥前二者均不执行操作 |
| 部署 | Docker → 通过 Terraform 部署到托管平台(类似 Render) |
| 代码检查/测试 | ruff + pytest + pytest-asyncio,以 Makefile 作为入口 |
如果没有理由偏离默认方案,请采用表中的每一项。本技能其余部分介绍“如何”操作,参考文件中则包含可直接复制粘贴的模板。
包布局
采用单个分层包,按领域拆分路由。业务逻辑绝不放在路由中。
src/myapp/
main.py # app + middleware + include_router() in a loop
config.py # pydantic-settings Settings + get_settings()
db.py # lazy async engine/sessionmaker + get_session() dep
models.py # SQLAlchemy 2.0 DeclarativeBase, Mapped[]
schemas.py # Pydantic request/response models
security.py # hashing, tokens, auth dependencies
observability.py # Sentry/PostHog init (guarded)
api/ # one APIRouter module per domain (+ deps.py, webhooks.py)
services/ # business logic; external SDKs isolated here
migrations/versions/ # Alembic
tests/ # pytest, mirrors api/ + services/
terraform/ # render.tf, stripe.tf, posthog.tf, variables.tf
Dockerfile docker-compose.yml pyproject.toml uv.lock alembic.ini Makefile
路由 → 服务 → 模型。 路由负责解析/验证并调用服务。服务负责承载逻辑,并处理所有外部 SDK 调用(Stripe、电子邮件、大模型),以便进行模拟。模型只负责持久化。
应用接线
在模块级定义 app(也可以使用工厂,但没有必要),通过循环注册路由器,将横切关注点作为中间件,并提供一个能验证数据库可连接的健康检查。完整模板见 STACK.md。
# main.py (shape)
app = FastAPI(docs_url=None if settings.is_prod else "/docs")
app.add_middleware(CORSMiddleware, allow_origins=settings.cors_origins, ...)
# request-id + security-headers middleware, slowapi rate limiter
for router in (auth.router, billing.router, webhooks.router, ...):
app.include_router(router)
@app.get("/health")
async def health(db: SessionDep):
await db.execute(text("SELECT 1")) # 503 if the DB is down
return {"status": "ok"}
将依赖项共享为 Annotated 类型别名——这样在函数签名中一目了然:
# api/deps.py
SessionDep = Annotated[AsyncSession, Depends(get_session)]
UserDep = Annotated[User, Depends(require_user)]
数据库
采用惰性单例异步引擎,设置 pool_pre_ping=True、expire_on_commit=False,使用 get_session() 异步生成器依赖项,并提供 URL 规范化器,将平台提供的 postgres:// URL 转换为 postgresql+asyncpg://。生产环境使用 Postgres,本地/CI 快速测试使用 SQLite (aiosqlite)。 通过 Alembic 管理迁移,并在部署的预部署步骤中应用。参见 STACK.md。
配置与密钥
使用一个带环境变量前缀的 Settings(BaseSettings)、@lru_cache get_settings(),并配备验证器,确保生产环境不会在默认密钥或缺少密钥的情况下启动。提交 .env.example,绝不提交 .env。计算型 @property 辅助函数(is_prod、cors_origins、billing_enabled)可让路由保持简洁。
支付
使用 Stripe,仅设置一个经过签名验证的 Webhook 端点,通过持久化事件表实现幂等性,并按事件类型分发;遇到未知事件时绝不返回 500。将每个 SDK 调用隔离在 services/stripe/ 模块中。完整模式(结账、订阅、Webhook 处理器、通过 Terraform 配置签名密钥)见 PAYMENTS.md。
你真正需要做出的决策
以上内容都是固定的。以下是每个应用真正需要做出的选择:
- 前端 — 服务端渲染的 Jinja + Tailwind/Alpine(无需构建 JS,快速交付) 或 独立部署在自身源站上的解耦 React/Vite SPA(界面更丰富,但组成部分更多)。 → FRONTEND.md
- 身份验证方案 — cookie-JWT(浏览器应用)、bearer-JWT/API-key(程序化访问)、 magic-link(无密码)或 OAuth(委托式)。bcrypt + 依赖项防护措施保持不变;只有令牌会不同。→ AUTH.md
- 后台任务 — 在请求中内联执行(最简单)、以 cron 作为工作进程(周期性/排队任务的默认方式),或使用 Redis 支持的任务队列(长轮询任务)。 只有在这三种方式都无法满足需求时才考虑使用 Celery。→ BACKGROUND_JOBS.md
- LLM 功能 — 使用
pydantic-ai和结构化的 Pydantic 输出,启动时必须提供密钥, 模型可通过 env 配置。→ FRONTEND.md 中有相关说明。
部署
使用单个 Docker 镜像(基于 astral-sh/uv,通过两阶段 uv sync 缓存层,
以非 root 用户运行,使用 uvicorn --proxy-headers)。同一个镜像通过不同命令运行 Web 服务、工作进程和迁移。目标是托管平台;使用 Terraform 为其配置资源(以代码管理应用 + Postgres + Stripe + PostHog),并将平台 Blueprint 文件作为文档说明的备用方案。迁移在部署前步骤中运行。密钥通过 Terraform 生成,或标记为 no-sync 并在其他渠道设置。→ DEPLOYMENT.md
审核清单
Structure:
- [ ] Logic in services/, not in route handlers
- [ ] External SDKs (Stripe/email/LLM) isolated behind a service module
- [ ] /health (or /readyz) actually pings the database
- [ ] Job status: failure writes its own terminal value, "in progress" is written
before the work is handed off, and the current value is read by monotonic id
Data:
- [ ] Async engine is a lazy singleton with pool_pre_ping
- [ ] postgres:// URLs normalized to postgresql+asyncpg://
- [ ] Every schema change has an Alembic migration
Config:
- [ ] Production refuses to boot on a default/missing secret
- [ ] CORS not "*" and HTTPS enforced in production
- [ ] .env.example committed; .env gitignored
Billing & security:
- [ ] Exactly one Stripe webhook, signature-verified, idempotent
- [ ] New passwords centrally limited to 72 UTF-8 bytes before bcrypt hashing
- [ ] Password verification remains unbounded for compatibility with existing hashes
- [ ] Passwords bcrypt-hashed; auth via reusable dependencies
- [ ] CSRF enforced globally for cookie auth (not per-form opt-in); no
state-changing GET requests — mutations use POST/DELETE
- [ ] Client IP for rate-limit keys/allowlists/audit logs taken from a fixed
position in the X-Forwarded-For chain (not the spoofable left-most value)
- [ ] Authentication rate limits use a required shared backend; backend outages
fail closed with 503, while healthy exhausted buckets return 429 + actual TTL
- [ ] Private OpenAPI schema served only by a custom guarded route (`openapi_url=None`)
- [ ] Sentry + PostHog init guarded (no-op when unkeyed, never break a request)
Ship:
- [ ] One Docker image; web/worker/migrate are just different commands
- [ ] Migrations run pre-deploy, not at app startup in prod
- [ ] Infra is in Terraform; secrets are not committed
参考文件
- STACK.md — pyproject、
main.py、db.py、config.py、Makefile 模板 - PAYMENTS.md — Stripe 结账 + 幂等 Webhook 模式
- AUTH.md — 四种令牌方案和通用依赖项防护措施
- BACKGROUND_JOBS.md — 内联执行、cron 工作进程和 Redis 队列的对比
- FRONTEND.md — Jinja/Tailwind/Alpine 与解耦 SPA 的对比(+ LLM 说明)
- DEPLOYMENT.md — Dockerfile、Terraform、迁移、密钥