Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

fastapi-service-template

A reusable Copier template for InfinitiBit FastAPI backends. It scaffolds a two-tier modular monolith designed to shed pieces into independent microservices later — without a rewrite.

Extracting a service = git mv src/services/<x> into a new repo + swapping one in-process public.py implementation for an HTTP client.

Every convention exists to make that sentence literally true — and the boundaries that guarantee it are enforced by import-linter in CI, not left to code review.


Quick start

You need uv (curl -LsSf https://astral.sh/uv/install.sh | sh). You do not need to clone this repo — Copier pulls it for you.

# Generate a new project. --trust lets the template provision Python + deps for you.
uvx copier copy --trust gh:InfinitiBit/fastapi-service-template my-app

Copier asks a few questions (see below), writes the project into my-app/, then runs cp .env.example .env, uv python install (the project's pinned Python, even if your system only has an older one), and uv sync. After it finishes:

cd my-app
uv run lint-imports                 # architectural boundaries (must be green)
uv run pytest                       # tests
uv run fastapi dev src/main.py      # run the app → http://localhost:8000/docs

That's it — a working, boundary-enforced FastAPI app on the right Python.

Without --trust, Copier still generates the project but skips those steps; it prints the commands (uv python installuv sync) to finish setup by hand. --trust is required because Copier never runs template shell commands unless you opt in.

The project pins its Python in .python-version and requires-python, so uv always selects (and installs) the correct interpreter — never the system one.

Pull template improvements into an existing project later

cd my-app
uvx copier update --trust            # re-applies the template, keeping your edits

This is the payoff of Copier over a plain clone: kernel fixes and new lint rules propagate to every generated project.

Questions the template asks

Question Effect
project_name Human-readable name → README, app title.
project_slug Python package / distribution name (snake_case).
python_version requires-python, ruff target, pyright.
layout single-service vs modular-monolith — only tunes README framing; the bones are identical.
first_module_name Names the first slice under src/services/.
include_auth Scaffold platform/auth (a simple generic header-based auth).
include_integration_example Scaffold an example integrations/ module.

How a generated project is organised

src/
  kernel/         stateless infra — config, db, storage, broker, events, errors
  platform/       PLATFORM CORE — always co-located (auth, registry); one DB, normal FKs
  services/       SERVICE CANDIDATES — extractable slices; own tables + bucket + workers + agent
  integrations/   INTEGRATION MODULES — external-system wrappers
  api/  main.py   composition root + app factory

Three archetypes, two tiers, one direction of dependency. The Shared Kernel holds stateless infra. The Platform Core is the identity/registry everything co-locates with (one DB, normal FKs — never split out). A Service Candidate is a self-contained domain that owns its tables, storage bucket, background workers, and optional AI pipelines, and is built to become its own service. Integration Modules each wrap one external system.

The rules (enforced by import-linter): dependency flows services → platform → kernel; services never import each other; platform never imports services; cross-module access is only ever through a module's public.py (DTOs + a Protocol), never another module's models/service/repository.

The full architecture — the reasoning, the data-ownership rules, the slice anatomy — is documented in the generated project's own README.md, and in this repo's design docs:

This repository's layout

copier.yml          # the questions, post-gen tasks (uv sync), and config
template/           # the project source (Copier _subdirectory)
                    #   .jinja files are rendered; everything else copied verbatim
                    #   templated path names produce the named slice and the
                    #   optional auth / integration dirs
CONTEXT.md          # architecture glossary (about the template; not copied into projects)
docs/               # ADRs + worked example (about the template; not copied into projects)
README.md           # this file

Generated projects intentionally do not inherit this repo's ADRs, glossary, or worked example — they get a clean docs/adr/ seeded for their own decisions.

Developing the template

# Render the template to a throwaway dir and verify it stays green:
uvx copier copy --trust --defaults -d project_name="Demo" -d first_module_name="widgets" . /tmp/gen
cd /tmp/gen && uv run lint-imports && uv run pytest

Stack

uv · ruff · pyright · pydantic v2 · async SQLAlchemy 2.0 · Alembic · taskiq · pytest + httpx · pre-commit · import-linter.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages