mcpField
B
building-python-web-apps

building-python-web-apps

github.com/wdm0006开发10962 次安装2 天前更新
代码仓库

关于 building-python-web-apps

Building Python Web Apps 面向独立开发者和小团队,指导使用 Python 构建中小型 SaaS 应用。它提供可重复采用的 FastAPI 技术栈、应用结构,以及认证、计费、后台任务和部署方案。

主要功能

  1. 01以 FastAPI 为核心的应用蓝图
  2. 02分层组织路由、服务与模型
  3. 03数据库、配置和认证模式
  4. 04Stripe 计费与 Webhook 指引
  5. 05Docker 和 Terraform 部署模式

适用场景

  1. 01启动小型 SaaS 应用
  2. 02选择应用结构与默认技术栈
  3. 03实现计费和 Webhook 处理
  4. 04规划部署与后台任务

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/wdm0006/python-skills --skill building-python-web-apps

安装后在 Claude Code 中输入/building-python-web-apps确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

AUTH.md
BACKGROUND_JOBS.md
DEPLOYMENT.md
FRONTEND.md
PAYMENTS.md
STACK.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、迁移、密钥

评论

支持 Markdown