Skip to content

script-logic/robyn-app-example-extended

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

12 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Python 3.14 Robyn uv Ruff Type checked: ty Docker Pydantic Structlog SQLAlchemy License: MIT

πŸ•΅οΈ Robyn Crime API

Extended Robyn app example.

Async REST API for managing crime records. Built with Robyn, Clean Architecture, production-ready tooling.


πŸ“‹ Table of Contents


✨ Features

  • Crime Record Management:

    • Create, read, update, and delete crime records
    • Paginated listing with configurable skip/limit
    • Full request/response validation with Pydantic v2
    • Structured error responses with request tracing
  • Async & High Performance:

    • Async Python web server (Robyn + Actix Rust runtime)
    • Async database operations (SQLAlchemy + aiosqlite)
    • WAL-mode SQLite with performance tuning
    • Request duration tracking in nanoseconds
  • Clean Architecture:

    • Strict separation of concerns (Domain, Database, Endpoints, Config)
    • Repository pattern with pure async functions
    • DI/IoC container with lazy loading and request-scoped providers
    • Interface-based design with adapter pattern
  • Enterprise Logging:

    • Structured logging with structlog
    • JSON output for production, colored console for development
    • Context binding with automatic request_id injection
    • File rotation with size limits
    • Third-party logger hijacking (SQLAlchemy, Robyn, Pydantic, etc.)
  • Production Ready:

    • Full type hints with Python 3.14
    • Ruff for linting and formatting (all rules enabled)
    • ty for strict type checking
    • Multi-stage Dockerfile with non-root user
    • Health check endpoint
    • CORS configuration
    • Bearer token authentication (extensible)

πŸš€ Quick Start

Prerequisites

  • Python 3.14+
  • uv
  • Docker (optional)

Installation

# Clone the repository
git clone https://github.com/script-logic/robyn-app-example-extended.git
cd robyn_app_example_extended

# Install with uv
uv sync

Run the Server

# Start the server
uv run python -m robyn_example

# Using Docker
docker-compose up

# Using Just
just run

The API will be available at http://127.0.0.1:8082.


πŸ“ Project Structure

β”œβ”€β”€ local_database/                       # SQLite database (WAL mode)
β”‚   └── database.db
β”œβ”€β”€ logs/                                 # Application logs (rotating)
β”œβ”€β”€ src/
β”‚   └── robyn_example/
β”‚       β”œβ”€β”€ adapters/                     # Adapter layer
β”‚       β”‚   └── adapters.py               # Config β†’ LoggerConfig adapter
β”‚       β”œβ”€β”€ config/                       # Configuration
β”‚       β”‚   β”œβ”€β”€ models/                   # Settings models
β”‚       β”‚   β”‚   β”œβ”€β”€ application_settings.py
β”‚       β”‚   β”‚   β”œβ”€β”€ cors_settings.py
β”‚       β”‚   β”‚   β”œβ”€β”€ database_settings.py
β”‚       β”‚   β”‚   β”œβ”€β”€ filesystem_settings.py
β”‚       β”‚   β”‚   └── logger_settings.py
β”‚       β”‚   └── app_config.py             # Pydantic-settings root
β”‚       β”œβ”€β”€ database/                     # Database layer
β”‚       β”‚   β”œβ”€β”€ crime_repository.py       # Pure async repository functions
β”‚       β”‚   β”œβ”€β”€ db_manager.py             # Engine & session management
β”‚       β”‚   └── models.py                 # SQLAlchemy 2.0 ORM models
β”‚       β”œβ”€β”€ di/                           # Dependency Injection
β”‚       β”‚   β”œβ”€β”€ _proxy_ioc.py             # Lazy-loaded proxy container
β”‚       β”‚   └── _real_ioc.py              # Real DI container
β”‚       β”œβ”€β”€ domain/                       # Domain entities
β”‚       β”‚   └── entities.py               # CrimeEntity, RequestMiddlewareEntity
β”‚       β”œβ”€β”€ logger/                       # Structured logging system
β”‚       β”‚   β”œβ”€β”€ handlers.py               # Console & file handlers
β”‚       β”‚   β”œβ”€β”€ interfaces.py             # LoggerConfig dataclass
β”‚       β”‚   β”œβ”€β”€ processors.py             # Log processors & cleaners
β”‚       β”‚   β”œβ”€β”€ renderers.py              # JSON & console renderers
β”‚       β”‚   └── setup.py                  # Logging initialization
β”‚       β”œβ”€β”€ robyn/                        # Web layer
β”‚       β”‚   β”œβ”€β”€ endpoints/
β”‚       β”‚   β”‚   β”œβ”€β”€ api_v1/               # API v1 endpoints
β”‚       β”‚   β”‚   β”‚   β”œβ”€β”€ endpoints.py      # CRUD route handlers
β”‚       β”‚   β”‚   β”‚   β”œβ”€β”€ enums.py          # Path parameter enums
β”‚       β”‚   β”‚   β”‚   └── schemas.py        # Request/response schemas
β”‚       β”‚   β”‚   β”œβ”€β”€ exceptions/           # Error handling
β”‚       β”‚   β”‚   β”‚   β”œβ”€β”€ exceptions_handler.py
β”‚       β”‚   β”‚   β”‚   └── schemas.py        # Error response models
β”‚       β”‚   β”‚   β”œβ”€β”€ helpers/              # Parsers & policies
β”‚       β”‚   β”‚   └── health.py             # Health check endpoint
β”‚       β”‚   β”œβ”€β”€ auth.py                   # Authentication handler
β”‚       β”‚   └── runner.py                 # App bootstrap & middleware
β”‚       └── __main__.py                   # Entry point
β”œβ”€β”€ pyproject.toml                        # Project metadata & dependencies
β”œβ”€β”€ docker-compose.yml                    # Docker Compose setup
β”œβ”€β”€ Dockerfile                            # Multi-stage Docker build
β”œβ”€β”€ Justfile                              # Task runner
β”œβ”€β”€ ruff.toml                             # Linter configuration
└── ty.toml                               # Type checker configuration

🎯 API Endpoints

Base URL: http://127.0.0.1:8082/api/v1

Method Endpoint Auth Description
POST /crime/add No Create a new crime record
GET /crimes/get?skip=0&limit=100 No List crimes (paginated)
GET /crime/:crime_id Bearer Get crime by ID
PUT /crime/update/:crime_id No Update crime by ID
DELETE /crime/:crime_id No Delete crime by ID

Health Check: http://127.0.0.1:8082/health

Request/Response Examples

Create Crime:

POST /api/v1/crime/add
{
    "type": "Robbery",
    "description": "Bank robbery at Main Street",
    "location": "Downtown",
    "suspect_name": "John Doe",
    "date_time": "2026-05-14T12:00:00",
    "latitude": 40.7128,
    "longitude": -74.0060
}

Response:

{
    "id": 1,
    "type": "Robbery",
    "description": "Bank robbery at Main Street",
    "location": "Downtown",
    "suspect_name": "John Doe",
    "date_time": "2026-05-14T12:00:00",
    "latitude": 40.7128,
    "longitude": -74.0060,
    "created_at": "2026-05-14T12:00:00+00:00",
    "updated_at": null
}

Error Response (500):

{
    "status_code": 500,
    "description": "Internal server error",
    "request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

πŸ— Architecture

Layer Diagram

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         robyn/ (Web Layer)          β”‚
β”‚    endpoints/ auth/ runner.py       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚      database/ (Data Layer)         β”‚
β”‚  crime_repository.py db_manager.py  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚       domain/ (Domain Layer)        β”‚
β”‚            entities.py              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    config/ di/ adapters/ logger/    β”‚
β”‚  (Infrastructure & Cross-cutting)   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Design Decisions

  1. Pure Repository Functions: Database operations are async functions, not class methods. Session is passed explicitly β€” no hidden state, easy to test.
  2. Lazy DI Proxy: The Ioc container uses a proxy pattern with LazyProvider to avoid circular imports and enable lazy resolution.
  3. ContextVar-based Request Tracing: request_id is stored in contextvars.ContextVar and propagated to middleware, exception handlers, and error responses without passing through function signatures.
  4. Adapter Pattern: LoggerConfigAdapter transforms AppConfig into a LoggerConfig dataclass β€” keeps logger module independent of config structure.
  5. SQLite WAL Mode: Write-Ahead Logging enables concurrent reads without blocking, with performance PRAGMAs tuned for API workloads.

πŸ“ Logging System

A sophisticated, production-ready logging system built with structlog.

Features

  • Structured Logging: All logs are structured events with automatic context injection (filename, function, line number, thread, process).
  • Dual Output:
    • Development: Colored console output with padded event names.
    • Production: JSON format with orjson serialization for log aggregation.
  • Request Tracing: Every request gets a UUID request_id that appears in all related logs and error responses.
  • Middleware Logging: Request body, headers, IP, and identity are logged at DEBUG level; duration is tracked in nanoseconds.
  • File Rotation: Configurable log rotation with size limits and backup count.
  • Third-party Hijacking: Automatically configures log levels for SQLAlchemy, Robyn, Pydantic, aiosqlite, and more.

Log Output Examples

Console (Development):

2026-05-14 12:00:00 [info     ] request_id=a1b2c3d4... runner.py:45
2026-05-14 12:00:01 [info     ] request_id=a1b2c3d4... request_path=/api/v1/crime/1 response_duration_ns=1234567 runner.py:58

JSON (Production):

{"event": "request_id=a1b2c3d4...", "logger": "robyn_example.robyn.runner", "level": "info", "timestamp": "2026-05-14T12:00:00", "filename": "runner.py", "lineno": 45}

βš™οΈ Configuration

Environment Variables

All settings can be overridden via environment variables with __ as nested delimiter:

ENV_FILE_NAME=".env.prod"
APP__TITLE="Crime API Production"
APP__HOST="0.0.0.0"
APP__PORT=8080
DB__DB_DIR="/data/database"
DB__ECHO=false
LOG__LOG_LEVEL=20
LOG__ENABLE_FILE_LOGGING=true

Default Settings

# Application
app.title = "Robyn Example"
app.host = "127.0.0.1"
app.port = 8082
app.client_timeout_sec = 30
app.keep_alive_timeout_sec = 20

# Database
db.db_schema = "sqlite+aiosqlite"
db.db_dir = "./local_database"
db.db_name = "database.db"
db.pool_size = 3

# Logging
log.log_level = 10  # 10 means "DEBUG", 20 means "INFO", etc
log.console_colors = True
log.enable_file_logging = True

# File System
filesys.logs_dir = "./logs"
filesys.max_log_file_size_mb = 10
filesys.log_backup_count = 5

πŸ›  Development

Setup

# Install with dev dependencies
uv sync --group dev

# Run linter & formatter
just lint

Code Quality

  • Ruff: All rules enabled (select = ["ALL", "ANN", "I"]), line length 79, Google-style docstrings.
  • ty: Strict type checking with all = "error".

Docker

# Build and run
docker-compose up --build

# Rebuild
docker-compose down && docker-compose build && docker-compose up

# Using Just
just build

The Docker image uses a multi-stage build with uv for fast dependency installation and runs as a non-root user.


πŸ“ License

MIT License – free to use and modify.


Built with ❀️ using Robyn, SQLAlchemy, structlog, that-depends and pydantic

About

⚑ Python 3.14 with speed of light! 🌠 Example of Rust-backed async app on Robyn Web Framework + Clean Architecture, DI/IoC by that-depends, structlog, SQLAlchemy 2.0 πŸ”₯ 99/100 code quality

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors