Skip to content

⚡ Robyn DDD Template - production-ready: strict layers, async SQLA/Alembic, msgspec, Loguru, Valkey/Redis, SMTP, Docker+uv.

Notifications You must be signed in to change notification settings

Lehsqa/robyn-backend-template

Repository files navigation

Robyn DDD Template

A Robyn-based service that follows a classic DDD layering (config ➜ infrastructure ➜ domain ➜ operational ➜ presentation) while using msgspec for fast JSON, SQLAlchemy 2.x async sessions, Valkey (Redis-compatible) caching, Loguru logging, and Passlib password hashing. The code lives under src/app and ships with a Docker Compose stack for PostgreSQL, Redis, a local SMTP sink, and the app container.

Project layout

├── compose/
│   └── app/
│       ├── Dockerfile             # container build
│       ├── dev.sh                 # run Robyn in dev mode
|       └── prod.py                # run Robyn in prod mode
├── docker-compose.yml
├── pyproject.toml
└── src/app
    ├── config/                    # Pydantic settings (env-driven)
    ├── infrastructure/            # DB/cache/mail/auth abstractions
    ├── domain/                    # Entities, constants, repository, services
    ├── operational/               # Application workflows (user flows, etc.)
    ├── presentation/              # Robyn route registrations
    └── server.py                  # Robyn entrypoint (logs + route wiring)

Local development

Requirements: Python 3.12+, uv (optional but handy), and a PostgreSQL + Valkey (or Redis) instance (or use the Docker Compose stack below).

uv venv
source .venv/bin/activate
uv pip install -e .[dev]

The [dev] extra adds the linting, formatting, testing, typing, and coverage helpers (ruff, flake8, black, isort, pytest/pytest-cov, coverage, mypy, and pyright) that are configured via pyproject.toml.

Copy .env.example to .env before running the app locally or via Compose; .env.example defaults to SQLite/fakeredis/localhost so the service can start without external infrastructure, and Compose overrides the database/cache hosts to point at its PostgreSQL/Valkey containers.

Makefile shortcuts

The repository ships with a Makefile that wraps the most common uv, Alembic, and Docker commands described throughout this README:

  • make install / make install.dev – install the project (with or without the [dev] extras) into the active virtual environment.
  • make sync / make lock – sync or lock dependencies via uv.
  • make migration.revision MIGRATION_MESSAGE="add table" – create an Alembic revision; make migration.upgrade – upgrade to the latest migration.
  • make run – start app.server in production mode; make run.dev – run Robyn with --dev; make run.compose – build and run the entire Docker Compose stack.
  • make tests / make tests.coverage – run pytest, optionally with coverage output.
  • make fix – run black, isort, and ruff --fix; make check – run the full lint/type/test suite; make check.types – run mypy only.
  • Docker helpers: make backend.run (start only the app container), make backend.status (tail app logs), make backend.update (rebuild without cache), and make backend.stop (tear down the stack).

Configuration

All runtime settings are provided by pydantic-settings with SETTINGS__ prefixes. A few useful overrides:

export SETTINGS__DATABASE__DRIVER=postgresql+asyncpg
export SETTINGS__DATABASE__HOST=localhost
export SETTINGS__DATABASE__PORT=5432
export SETTINGS__DATABASE__USER=app
export SETTINGS__DATABASE__PASSWORD=app
export SETTINGS__DATABASE__NAME=robyn_users
export SETTINGS__CACHE__HOST=localhost  # Valkey/Redis host
export SETTINGS__CACHE__PORT=6379
export SETTINGS__CACHE__DB=0
export SETTINGS__CACHE__USE_FAKE=true  # use fakeredis from the [dev] extras
export SETTINGS__MAILING__HOST=localhost
export SETTINGS__MAILING__PORT=1025
export SETTINGS__MAILING__SENDER_EMAIL=no-reply@example.com
export SETTINGS__MAILING__SENDER_NAME="Robyn Demo"

Installing the [dev] extras gives you fakeredis so you can keep SETTINGS__CACHE__USE_FAKE=true and avoid provisioning a Redis server during local development or tests.

If you omit these, the service defaults to an on-disk SQLite database (sqlite+aiosqlite) and a cache at cache:6379, which is ideal for the Compose stack.

Running the server

The Robyn entrypoint lives in app.server and starts after the infrastructure is ready. Before you run it, source .env (copied from .env.example) so SETTINGS__* picks up the configured driver/hosts:

source .env
uv run python -m app.server

Run the database initialization steps above before invoking it so the schema is up to date.

You can use the Makefile equivalents instead of invoking the module manually:

  • make run wraps uv run -- python -m app.server --fast --log-level WARNING.
  • make run.dev uses the Robyn CLI to start src/app/server.py with --dev.

Set SETTINGS__DEBUG=false in production to silence Alembic/SQLAlchemy info logs before the log file is written (compose/app/dev.sh still applies migrations first).

Endpoints currently wired up in presentation/healthcheck.py and presentation/users.py include:

  • GET /health – simple readiness probe.
  • POST /users – create an internal user (hashes passwords, enqueues activation email).
  • POST /users/activate – activate a user via cached activation key.
  • POST /users/password/*, /users/email-change/* – password/email lifecycle flows.
  • POST /auth/login – exchange valid credentials for a bearer token.
  • GET /auth/me – decode the bearer token from Authorization and return its claims.

Responses are serialized with msgspec for speed, and domain objects are validated via Pydantic models (PublicEntity, etc.).

Development tooling

Install the [dev] extras (see above) to unlock the linting/formatting/testing/typing helpers whose configuration lives in pyproject.toml. Recommended commands:

ruff check src/app
ruff check --fix src/app
black .
isort .
flake8 src/app

python -m pytest already runs with coverage (htmlcov/index.html) and clears cache on every invocation.

For static typing, run:

mypy src/app
pyright

Makefile shortcuts:

  • make fix runs black, isort, and ruff --fix on the codebase.
  • make check runs the full lint/type/test suite (ruff, black --check, isort --check, mypy, pytest).
  • make check.types runs mypy with --check-untyped-defs.
  • make tests and make tests.coverage wrap the pytest commands above.

Docker Compose

The included docker-compose.yml provisions everything needed for local testing:

docker compose up --build

Or use the Makefile helpers:

  • make run.compose – same as docker compose up --build.
  • make backend.run / make backend.stop – start or stop the app service only.
  • make backend.status – tail the last 100 lines of app logs.
  • make backend.update – rebuild the app image without cache.

Services:

  • postgres – PostgreSQL 16 with database robyn_users (user/pass app).
  • cache – Valkey (Redis-compatible) cache used by CacheRepository.
  • mailhog – SMTP sink (1025) with web UI at http://localhost:8025 for captured mail.
  • app – Robyn service (python -m app.server) exposed on http://localhost:8000. The environment block in docker-compose.yml overrides only the database/cache driver and host entries; the rest load from .env so the same file works for both Docker and local runs.

The app service builds from compose/app/Dockerfile, copies the dev.sh helper into the image, and runs it so Alembic upgrades happen before python -m app.server --reload executes.

The Dockerfile also exposes a production target:

docker build -f compose/app/Dockerfile --target prod -t robyn-app:prod .

That image is based on distroless Debian, bundles the virtual environment created via uv sync --frozen --no-dev, and uses compose/app/prod.py to apply migrations before starting the server.

Database migrations (Alembic)

Alembic is configured with async engines and uses the Pydantic settings (so the same SETTINGS__* overrides apply). Generate and apply migrations with the commands below. The container entrypoint (compose/app/dev.sh) already runs alembic upgrade head before launching python -m app.server --reload, so you only need to run those commands when the schema changes.

export PYTHONPATH=src  # ensure `app` package is importable
alembic revision -m "describe change"
alembic upgrade head

Equivalent Makefile commands:

  • make migration.revision MIGRATION_MESSAGE="describe change"
  • make migration.upgrade

If you run inside Docker, execute the same commands through docker compose exec app ....

The compose service starts in development mode by exporting SETTINGS__DEBUG=true, sets LOGURU_LEVEL=DEBUG, and mounts ./logs into the container so Loguru’s logs/robyn_app.log is immediately available on the host. Tail the file locally or inspect docker compose logs -f app for streaming output.

Environment variables are passed to the container via SETTINGS__* prefixes so the app picks up the correct infra hosts/ports. Logs are written via Loguru to logs/robyn_app.log inside the container (and mounted working tree if you bind-mount).

To run migrations or inspect the DB, connect to localhost:5432 using the credentials above. Valkey listens on localhost:6379 (compatible with any Redis client).

Notes

  • Schema creation now happens through Alembic migrations (alembic upgrade head must be run before starting the service, and compose/app/dev.sh runs the same upgrade before the container launches python -m app.server --reload).
  • Outbound mail is delivered through the async SMTP client in infrastructure/mailing/services.py; in Docker it targets MailHog, while in local dev you can point the settings at any SMTP server.

About

⚡ Robyn DDD Template - production-ready: strict layers, async SQLA/Alembic, msgspec, Loguru, Valkey/Redis, SMTP, Docker+uv.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published