🐍 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.
👋 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
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)
| 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)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| 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.