Skip to content

Architecture preset / feature matrix

Interactive fastkit init --interactive asks for an architecture preset (issue #44) before collecting feature selections. The preset shapes the generated project's layout — different presets ship a different base template and put generated config files in different locations so they sit next to the existing structure rather than in a parallel src/config/ tree.

This page is the source of truth for what each preset does, where files land, and which feature combinations require manual wiring.

Preset → base template

Preset Base template Description
minimal fastapi-empty Smallest viable FastAPI app — placeholder main.py is regenerated from your feature selections.
single-module fastapi-single-module Single-file FastAPI app — main.py is regenerated.
classic-layered fastapi-default Layered split (api/routes, crud, schemas, core). Shipped main.py is preserved.
domain-starter fastapi-domain-starter Domain-oriented (src/app/domains/<concept>/). Shipped main.py is preserved. Recommended default.

Generated file locations

Preset main.py overlay Database config target Auth config target
minimal regenerated at src/main.py src/config/database.py src/config/auth.py
single-module regenerated at src/main.py src/config/database.py src/config/auth.py
classic-layered preserved (template-shipped) src/core/database.py src/core/auth.py
domain-starter preserved (template-shipped) src/app/core/database.py src/app/core/auth.py

Database / auth feature support per preset

These features are supported across every preset — the package install always succeeds; the difference is whether the dynamic main.py overlay also wires them up automatically.

Feature minimal / single-module classic-layered / domain-starter
Database (PostgreSQL, MySQL, SQLite, MongoDB) Generates the config module and stubs await init_db() calls in the regenerated main.py. Generates the config module at the preset's path. The shipped main.py is preserved, so wire get_db() into routers manually.
Authentication (JWT, FastAPI-Users, OAuth2, Session-based) Generates the auth config module; JWT imports HTTPBearer, OAuth2 and Session-based also add their main.py middleware setup. Generates the auth config module at the preset's path. No imports added to main.py — wire dependencies manually.
Background tasks (Celery, Dramatiq) Generates worker.py and features/tasks.py, and mounts the task router in the regenerated main.py. Generates worker.py and features/tasks.py, but the shipped main.py is preserved — mount the router manually.
Caching (Redis) Generates features/cache.py and mounts/initializes it in the regenerated main.py. Generates features/cache.py; mount it manually.
Utilities: Pagination / WebSocket Generates features/pagination.py / features/websocket.py and mounts the router in the regenerated main.py. Generates the same module; mount it manually.
Monitoring: OpenTelemetry No standalone file — tracing setup is added directly to the regenerated main.py. No standalone file. The shipped main.py is preserved, so tracing is not wired — add the setup manually.
Migrations (Alembic) Generates alembic.ini, alembic/env.py, a baseline revision and scripts/migrate.sh for any SQL database selection — independent of the main.py overlay. Same — Alembic generation does not depend on whether main.py is preserved.
Tooling (ruff, pre-commit, github-actions, devcontainer, makefile) Generates the matching config file(s), independent of the main.py overlay. Same.
Logging (structured) Generates logging_config.py and wires the request-id middleware into the regenerated main.py. Generates logging_config.py; the shipped main.py is preserved, so wire the middleware manually.
CORS (utility) CORSMiddleware added to the regenerated main.py with allow_origins=['*']. Already wired in the shipped main.py (conditional on settings.all_cors_origins). Activate by setting BACKEND_CORS_ORIGINS in .env — no code edits required.
Testing (Basic / Coverage / Advanced) pytest.ini is generated at the project root; Advanced also adds tests/factories.py and tests/test_factories.py. Same.
Deployment (Docker, docker-compose) Dockerfile and/or docker-compose.yml written at the project root. Same.
/health / /ready Always present in the regenerated main.py. Already present in the preserved, template-shipped main.py.

When you'll see a "Preset compatibility" warning

For presets that preserve the shipped main.py (classic-layered, domain-starter), some feature selections won't be auto-wired into the app. The CLI surfaces a one-shot warning at the end of generation listing which selections need manual wiring:

Selected feature Triggers a warning under classic-layered / domain-starter?
CORS (utility) ❌ — already wired in the shipped main.py. Just populate BACKEND_CORS_ORIGINS in .env.
Rate-Limiting (utility) ✅ — slowapi limiter setup is not added
Pagination / WebSocket (utility) ✅ — the generated router (features/pagination.py / features/websocket.py) is not mounted
Celery / Dramatiq (async_tasks) ✅ — the generated worker.py / features/tasks.py router is not mounted
Redis (caching) ✅ — the generated features/cache.py is not mounted or initialized
Prometheus / OpenTelemetry (monitoring) ✅ — instrumentation setup is not added to main.py
structured (logging) ✅ — logging_config.py is generated, but the request-id middleware is not added to main.py
Alembic (migrations), tooling selections ❌ — these files are generated and usable regardless of preset; nothing depends on main.py
Any database / auth selection ⚠️ — config files are generated, but you must Depends() them into your routers

For minimal and single-module presets the dynamic main.py overlay handles every axis above automatically; no warnings fire.

Unsupported combinations (stay safe)

The strategist deliberately does not attempt to splice generated code into a template-shipped main.py. Doing so would risk producing broken imports or duplicating routers. The contract is:

  • Selected packages are always installed (so pip freeze matches the user's intent).
  • Generated config modules always land at the preset-appropriate path.
  • For preserve-main presets, the user is told which selections still need manual wiring instead of getting silently broken code.

If you need full auto-wiring of every feature, pick minimal or single-module — they regenerate main.py from feature flags.