Skip to main content

🐍 PyFr

A cookiecutter template for production-ready Python microservices.

Answer a few prompts. Get a service that runs, is tested, is observable, and is documented.

CI Full suite Docs Python License: MPL 2.0 Status: usable template

📖 Read the documentation →


👋 Welcome

Every team starts a new Python service the same way: wiring up logging, health checks, configuration, error formats, a container image. It takes two to four weeks, and every team's result is a little different.

PyFr aims to make that a ten-minute step instead — and to make the result the same good one every time.

The generated code is yours. 🎁 A generated service imports no PyFr package: no framework to upgrade, no runtime dependency on us, and nothing to lock you in. The one thing PyFr publishes is the updater, pyfr-cli, which runs at update time and is never imported. A generated project still receives later template fixes: just update pulls them into itself through an ordinary git merge.

✅ Status: M0–M8 done

The template is usable — uvx cookiecutter gh:EmadMokhtar/pyfr generates a project whose just check passes: three combinations are rendered for real and run through their own just check-all on every merge to main and every night, and all eight are rendered and lint-checked on every pull request and merge. M7, the conversion into a template, is complete: the template renders from twelve prompts, prunes the backends you do not choose, gives a generated project its own workflows and its own documentation site about itself, and records the template version it came from. M8, template updates, is complete too: just update pulls a later template version into a generated project through a git merge, and a weekly workflow opens the pull request for it. See the roadmap for what each milestone delivered. The reference service is rendered from that template; it is the complete, running service you can run, read, and copy from right now.

PyFr is built in three phases:

Phase Milestones What happens
A M0–M6 Build the reference service as ordinary Python — no template placeholders anywhere
B M7 Convert it into the cookiecutter template ✨
C M8, then forever Keep the two in step: the template stays the source of truth, and a generated project pulls later versions into itself 🔁

The rule behind that order: never debug Jinja and Python at the same time. 🙂

M0 through M8 — the reference service, its conversion into a template, and template updates for generated projects — are complete. See the roadmap for what each milestone delivered.

🚀 Try it in one command

uvx cookiecutter gh:EmadMokhtar/pyfr

cookiecutter asks twelve questions — the project's name, its author, which of PostgreSQL, Redis and S3 it gets, its port, its licence — and every one has a default. The hook then prunes what you did not choose, runs git init, uv sync and pre-commit install, and makes the first commit, each step best effort, so an offline laptop still gets a whole project.

Then cd into it, just up, and open http://localhost:8000/docs. 👋

The getting started guide explains each prompt and what the hook does; the generated project's own site takes it from there.

📦 What's in the box

Every item below already runs in the reference service today — nothing here is a promise:

  • ⚡ FastAPI, with an application factory and a lifespan.
  • 🧱 Four layers — domain, services, infrastructure, api — with the dependency rule enforced by a build check, not by code review.
  • ✅ Configuration validated at startup. A bad value stops the process with a readable message and exit code 78, instead of causing a 500 an hour later.
  • 📋 Structured logging — one JSON object per line on standard output, with third-party records passing through the same chain.
  • 💚 Three health endpoints answering three different questions. Liveness never checks a dependency, so a database hiccup cannot restart every instance at once.
  • 🚨 RFC 9457 Problem Details error responses.
  • 🔗 Correlation identifiers binding one value to every log line a single request produced.
  • 🛑 Graceful shutdown, so a rolling deployment does not drop live requests.
  • 🔒 A hardened container image: non-root, no build tools, no shell utilities in the final layer, and reproducible installs from a lock file.
  • 📖 Its own documentation site, built from its own docs/ and deployed to GitHub Pages by its own workflow — the reference service's site is the worked example.

M1–M6 have since added persistence, OpenTelemetry, contract testing, cache and object storage, release automation, and supply-chain auditing, scanning and publishing — the roadmap lists what each one delivered.

📚 Documentation

Two sites, deployed together. PyFr's site is about the template: generating a project, how the template is built and tested, and why it is a template rather than a framework. The reference service's site is about the service: it is the documentation every generated project ships about itself, rendered here for the reference answers.

Page What it covers
🏁 Getting started Generate a project: the twelve prompts, the hook, the first just up
🗺️ Roadmap What ships when
🤝 Contributing Working on PyFr itself: the template body, the golden diff, pruning, the two sites
🏁 Run the service Place an order and read each response
🔨 Add an endpoint Build a feature through all four layers
🏛️ Architecture How the pieces fit, and why
⚙️ Configuration Every environment variable

The design specification and milestone plans live in docs/superpowers/. They contain the reasoning behind the decisions the site describes, and are deliberately not published.

🛠️ Development

The repository root and the reference service are two separate Python projects, each with its own pyproject.toml. They are never synced together. The template body, {{cookiecutter.project_slug}}/, is the source of truth: edit it, run just regen, and the reference service is rendered from it — never edit examples/reference-service/ by hand.

# The template and its tests: both sites build, the root suite, the golden diff
uv sync --group dev --group docs && just check
# The reference service
cd examples/reference-service && uv sync && just check
# PyFr's documentation site, with a live preview
just docs-install && just docs

The contributing guide has the whole loop: the regeneration and golden diff, backends and pruning, the two documentation sites and every root just recipe.

🤝 Contributing

Issues, questions, and pull requests are all welcome — including "I read this and it didn't make sense," which is one of the most useful things you can send while the project is this young.

Commit messages and pull request titles must follow Conventional Commits. The contributing guide has the rest.

🙏 Credits

PyFr's architecture is influenced by GoFr, the Go microservice framework — the context-driven layering and the emphasis on developer experience, adapted to Python's ecosystem and to a template rather than a library.

⚖️ License

Mozilla Public License 2.0.

Metadata

Release files for pyfr-cli 0.14.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 pyfr-cli 0.14.1
File Size Uploaded Provenance Actions
pyfr_cli-0.14.1.tar.gz 42.7 kB Without provenance Details for pyfr_cli-0.14.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for pyfr-cli 0.14.1
File Interpreter ABI Platform Actions
pyfr_cli-0.14.1-py3-none-any.whl Python 3 none any Details for pyfr_cli-0.14.1-py3-none-any.whl

Total release size: 84.5 kB

Release files / pyfr_cli-0.14.1.tar.gz

Download URL pyfr_cli-0.14.1.tar.gz
Size 42.7 kB
Tags Source
SHA-256 checksum
How to use checksums
18e9c9afe5c972c383038ad468e0baa91ec963c43117d32781045812752e9783
BLAKE2b-256 checksum
How to use checksums
b2c8e2561eb2590a421ae501d76bcff853a1b78c2255a00d9ed5b264ce43a57c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","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 / pyfr_cli-0.14.1-py3-none-any.whl

Download URL pyfr_cli-0.14.1-py3-none-any.whl
Size 41.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e0dcf0d241cf84734c028bf0beec043b60fea7cebb1f517eba83fcbde284da98
BLAKE2b-256 checksum
How to use checksums
a0d70ccd2e365d819a5984f0c3ab5efa6885e74691c6fd00e10571170716930f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","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}

Security for pyfr-cli 0.14.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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