Skip to main content

feathers

Scaffold production FastAPI services from one YAML file — in under 10 seconds.

PyPI · Why · Architecture · Demo · Schema Example

pypi python ci coverage mypy license


What it is

feathers is a CLI that reads a single YAML schema and emits a production-ready, MVC-structured FastAPI service — with tests, migrations, observability, CI, platform middleware, and Docker, all wired up and running.

Unlike a cookiecutter (which gives you a dead tree the moment you touch it), feathers add uses libcst AST rewriting to slot new code into existing services without clobbering a single line you wrote. Regeneration stays safe for the lifetime of the project.


Why it's different

Most scaffolders feathers
Cookiecutter templates — stale after first edit Incremental AST codegen — regeneration stays safe forever
String templating Pydantic v2 schema validated before any file is written
Hand-wired middleware per service Platform middleware shipped with every generated service
You protect your edits with prayer Fence markers (# feathers: begin hand-written) — regen never touches protected regions
Pick your own stack Opinionated & consistent — FastAPI + uv + Alembic + structlog + Prometheus + OpenTelemetry

Quick start

pip install feathers-cli

feathers new --schema demos/users.yaml --name hello-users --out .
cd hello-users && make run
# → http://localhost:8000/docs

Add an endpoint later without regenerating the whole service:

# edit demos/users.yaml → append a new endpoint
feathers add endpoint --schema demos/users.yaml --service ./hello-users

Your hand-written code between # feathers: begin hand-written fences stays untouched.


CLI reference

Command Purpose
feathers new --schema FILE --name NAME --out DIR Generate a new service from a schema
feathers add endpoint --schema FILE --service DIR Slot a new endpoint into an existing service
feathers add model --schema FILE --service DIR Add a new model stub
feathers lint SCHEMA Validate a YAML schema without generating
feathers doctor Environment health check (Python, uv)
feathers bench (v0.2) Run Locust benchmarks against a generated service

How it works

flowchart TD
    YAML[schema.yaml] --> Loader[loader<br/>YAML → dict]
    Loader --> Schema[schema<br/>Pydantic v2 frozen validation]
    Schema --> Context[context<br/>type mapping · snake/pascal · plural]
    Context --> Renderer[renderer<br/>Jinja2 · 21 templates]
    Context --> Patcher[ast_patcher<br/>libcst · for feathers add]
    Renderer --> Service[Generated FastAPI service]
    Patcher --> Service

new vs add

sequenceDiagram
    participant User
    participant CLI as feathers CLI
    participant Val as Pydantic validator
    participant Gen as Jinja renderer
    participant AST as libcst patcher
    participant FS as File system

    User->>CLI: feathers new
    CLI->>Val: validate schema
    Val-->>CLI: ok
    CLI->>Gen: render 21 templates
    Gen->>FS: write full service tree

    User->>CLI: feathers add endpoint
    CLI->>Val: validate schema
    Val-->>CLI: ok
    CLI->>AST: patch routers/*.py
    AST->>FS: read existing file
    AST->>AST: find marker fences
    AST->>AST: insert between markers
    AST->>FS: write file (hand-written code untouched)

See docs/ARCHITECTURE.md for the deep dive.


Schema anatomy

service:
  name: hello_users
  description: A minimal users service
  python: "3.12"

models:
  - name: User
    fields:
      - { name: id,       type: uuid,     primary: true }
      - { name: email,    type: str,      unique: true, indexed: true }
      - { name: name,     type: str }
      - { name: created,  type: datetime }
    soft_delete: true
    audit: true

endpoints:
  - { method: GET,  path: /users/{id}, handler: user.get,    auth: any }
  - { method: POST, path: /users,      handler: user.create, auth: admin }
  - { method: GET,  path: /users,      handler: user.list,   auth: any, paginate: cursor }

observability:
  metrics: prometheus
  tracing: otel
  logging: structlog

deploy:
  target: render
  min_instances: 1
  health: /health

Every field is validated by frozen Pydantic v2 models — if the schema is wrong, feathers refuses to write a single file.


Generated service layout

hello-users/
├── src/hello_users/
│   ├── main.py                  # FastAPI app + middleware
│   ├── api/routers/             # One router per model
│   ├── services/                # Business logic layer
│   ├── repositories/            # Data access layer
│   ├── models/                  # Dataclass stubs (SQLAlchemy wiring in v0.2)
│   ├── schemas/                 # Pydantic DTOs
│   └── core/
│       └── platform.py          # /health, /version, X-Request-ID, X-Platform-Token
├── tests/
│   └── test_health.py
├── .github/workflows/ci.yml     # lint → test → build
├── Dockerfile
├── Makefile                     # make run | test | lint | format | typecheck
├── render.yaml                  # one-click Render deploy
└── pyproject.toml               # uv-managed

Tech stack

Concern Choice
CLI framework Typer
YAML validation Pydantic v2 (frozen models)
Template engine Jinja2 (21 templates per service)
AST rewriting libcst
Package manager uv
Lint / Types ruff + mypy strict
Tests pytest + coverage

Engineering philosophy

Principle How it shows up
Spec-TDD 45 tests across loader, schema, renderer, AST patcher, CLI. Red-first.
Negative-space programming Literal types for field types, HTTP methods, auth roles. Frozen Pydantic models. Schema validation rejects invalid input before any file is written.
MVC-style layering (adapted for CLI) cli → generator → schema. Each layer has one responsibility and never reaches across.
Typed everything mypy --strict passes. No any in source. Public APIs fully type-hinted.
Pure core, imperative shell Schema validation, context building, and rendering are pure. File I/O lives only in the CLI entry points.
One responsibility per module loader (I/O), service (schema defs), context (view transforms), renderer (Jinja), ast_patcher (AST).

Testing

make test                                  # full suite
uv run pytest --cov=src/feathers --cov-report=term-missing
uv run pytest -m "not slow"                # skip e2e generation + boot
Metric Value
Test count 45 tests
Coverage 86% (target: 100%)
E2E @pytest.mark.slow — generates the users service, uv sync, boots uvicorn, hits /health
CI GitHub Actions: ruff → mypy → pytest → uv build

Roadmap to 100% coverage

Module Current Gap
cli.py 73% add_endpoint / add_model error paths, name-mismatch warning
generator/context.py 88% plural() edge cases ("entry" → "entries", "status" → "status")
generator/renderer.py 92% Template load / render error paths
schema/loader.py 94% OSError on file read, YAML non-dict root
generator/ast_patcher.py 96% Missing routers_dir / models_dir error branches

Benchmarks (v0.2 target)

Metric Target
Generated GET /users/{id} throughput ≥ 10,000 req/s
Generated GET /users/{id} p99 latency < 30 ms

Run via feathers bench (Locust) against local Postgres.


Deployment

  • feathers-cli itself → published to PyPI on v* tag push via GitHub Actions
  • Generated services → deploy to Render, Fly.io, or Docker (target chosen in schema)

Contributing

git clone https://github.com/Abdul-Muizz1310/feathers.git
cd feathers
uv sync --all-extras
make test
make lint

Guidelines:

  1. Open an issue before non-trivial PRs
  2. Red-first TDD — failing test in the commit before the fix
  3. make lint typecheck test must pass

License

MIT — see LICENSE.


Built to make regenerating FastAPI services boring.

Metadata

Release files for feathers-cli 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for feathers-cli 0.1.1
File Size Uploaded
feathers_cli-0.1.1.tar.gz 16.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for feathers-cli 0.1.1
File Interpreter ABI Platform
feathers_cli-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 40.7 kB

Release files / feathers_cli-0.1.1.tar.gz

Download URL feathers_cli-0.1.1.tar.gz
Size 16.0 kB
Tags Source
SHA-256 checksum
How to use checksums
f4154469aac6fcca0662f30f418dcbf9cd03b1a73b2d101dfdc03be308671ebc
BLAKE2b-256 checksum
How to use checksums
e366bc1e2975c33eea8ab34f7bb7969e13ff776bd901a72c8e7d410d021c5e65
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.17 {"installer":{"name":"uv","version":"0.9.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / feathers_cli-0.1.1-py3-none-any.whl

Download URL feathers_cli-0.1.1-py3-none-any.whl
Size 24.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a394a6079027a3e3a15b73cbb0013365a570d4ede6bd42c3a6166a3a20acdd49
BLAKE2b-256 checksum
How to use checksums
553642f6ab5e97cc16e2432e25750134125bd4d7f404b3ef21ccba228a345262
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.17 {"installer":{"name":"uv","version":"0.9.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page