Async-first. Laravel-inspired. Built for Python 3.14+.
Quick Start • Code Tour • Features • Documentation • Community
The endpoint is just the beginning.
Then come the database, authentication, validation, file uploads, scheduled work, and all the decisions that turn a demo into an application.
Orionis is an async-first, full-stack Python framework that connects those pieces. A service container at the center. A fluent ORM for your data. Shared conventions across HTTP, console commands, and tests.
For APIs, server-rendered applications, and internal tools that need more than an HTTP layer, without leaving the Python ecosystem.
Early access: Orionis is in alpha. APIs may change before 1.0; this is not a production-stability promise. Try it on a real problem, tell us where it gets in your way, and help shape the framework.
- Structure that feels familiar. Laravel-inspired providers, controllers,
facades, migrations, and fluent APIs, built around Python's
async/await. - Dependencies declared, not hunted down. Type-hint a service contract; the container resolves it in controllers, commands, and test methods. Choose singleton, request-scoped, or transient lifetimes.
- APIs and HTML belong in the same app. Return JSON, render an async Jinja2 view, stream a file, or redirect with validation errors and old input.
- Your infrastructure has a home. Configuration, caching, storage, logging, and scheduling follow the same application lifecycle.
These are implemented capabilities, not a roadmap.
| Area | What you get today |
|---|---|
| HTTP | Granian with ASGI and RSGI, named routes, typed path parameters, middleware, CORS, rate limiting, streaming, and file responses. |
| Validation | msgspec schemas, nested payloads, field constraints, custom rules, and multi-error responses. Invalid API input becomes HTTP 422. |
| Authentication | Session login and opaque personal access tokens, request-scoped identity, roles, permissions, and resource policies. |
| ORM & Database | Async Active Record, a shared fluent query builder, relationships, eager loading, soft deletes, scopes, model events, pagination, transactions, and migrations. |
| Views & Sessions | Async Jinja2, CSRF integration, flash messages, old form input, error bags, and file, memory, cache, or database session stores. |
| Cache | Memory, file, Redis, Memcached, and database stores, with TTLs, counters, and lock APIs. |
| Storage | Local and in-memory drivers, uploads, streams, and optional S3, Azure Blob, and Google Cloud Storage integrations. |
| Console & Scheduling | Reactor commands, scaffolding, Rich output, and APScheduler tasks with memory, Redis, or database job stores. |
| Everyday Essentials | Argon2id and bcrypt hashing, AES encryption, rotating logs, translations, collections, and date/time utilities. |
| Testing | An integrated runner, async test cases, dependency injection, discovery, filtering, and CLI failure exit codes. |
Database backends include SQLite, PostgreSQL, MySQL, Oracle, and SQL Server. External services require configuration; cloud SDKs and some database drivers use the optional extras declared in pyproject.toml.
A place for every part of your application
app/
http/ Controllers, schemas, and middleware
models/ Application models
contracts/ Service interfaces
services/ Business logic
providers/ Dependency bindings and startup hooks
console/ Commands and schedules
bootstrap/ Application composition
config/ Typed application configuration
database/ Migrations
resources/ Views and translations
routes/ Web, API, and console entry points
tests/ Application tests
orionis/ Framework source in this development repository
You need Python 3.14+ and uv. To explore the current implementation, run this repository's development app:
git clone --branch=1.x https://github.com/orionis-framework/framework.git
cd framework
uv sync --python=3.14Copy .env.example to .env: use cp .env.example .env on
macOS/Linux or Copy-Item .env.example .env in PowerShell. Keep these local
settings for a first run without external database or cache servers:
DB_CONNECTION=sqlite
DB_DATABASE=database/database.sqlite
CACHE_STORE=memory
SESSION_DRIVER=fileOn Windows, set $env:PYTHONIOENCODING = "utf-8" before running Reactor.
uv run python reactor migrate
uv run python reactor serveOpen the address printed by serve. This is a framework development
checkout, not a generated application skeleton.
Already have a Python project? uv add orionis installs the package;
application bootstrap and configuration are still required.
Accept a project name, validate it, persist a model, and return 201 Created. The supporting model and migration are included below.
Declare the input in app/http/schemas/create_project.py:
from orionis.schemas import Schema
from orionis.schemas.constraints import MaxLength, MinLength
from orionis.schemas.fields import Field
class CreateProject(Schema):
name: Field[str, MinLength(3), MaxLength(120)]Write the action in app/http/controllers/project_controller.py:
from app.http.schemas.create_project import CreateProject
from app.models.project import Project
from orionis.http import HttpResponse, response
class ProjectController:
__slots__ = ()
async def store(self, payload: CreateProject) -> HttpResponse:
project = await Project.create(payload.toDict())
return response.json(project.toDict(), status_code=201)Register the route in routes/api.py:
from app.http.controllers.project_controller import ProjectController
from orionis.support.facades.router import Route
Route.post("/projects", [ProjectController, "store"]).name("projects.store")The container supplies a validated CreateProject instance before the action
runs. A missing, too-short, or too-long name produces 422 with field errors.
No manual body parsing or validation call in the controller.
Complete the example: model and migration
Create app/models/project.py:
from typing import ClassVar
from orionis.orm import BigInteger, Model, String
class Project(Model):
__slots__ = ()
fillable: ClassVar[list[str]] = ["name"]
timestamps: ClassVar[bool] = False
id = BigInteger().primary().autoIncrement()
name = String(120)Add m202609140001_create_projects_table.py under database/migrations/:
from orionis.database.contracts.migration import Migration
from orionis.support.facades.schema import Schema
class CreateProjectsTable(Migration):
__slots__ = ()
async def up(self) -> None:
async with Schema.create("projects") as table:
table.id()
table.string("name", 120)
async def down(self) -> None:
await Schema.drop("projects")Run uv run python reactor migrate again, then send a JSON request body such
as {"name": "Orionis Playground"} to the new route. This is a public demo
endpoint; add authentication and authorization for protected application data.
Inside an async controller or service, after applying the migration:
from app.models.project import Project
from orionis.support.facades.db import DB
page = await (
Project.where("name", "like", "Orionis%")
.orderBy("id", "desc")
.paginate(per_page=20, page=1)
)
rows = await DB.table("projects").select("id", "name").get()Models add hydration, casts, relationships, and lifecycle behavior. Direct table queries return records without requiring a model. Both use the same query language and SQLAlchemy Core's async engine, not SQLAlchemy's ORM session.
One entry point for the work around your application:
| Command | Purpose |
|---|---|
uv run python reactor serve |
Start the development HTTP server. |
uv run python reactor make:command DailyReport |
Scaffold an application command. |
uv run python reactor make:provider ProjectServiceProvider |
Scaffold a service provider. |
uv run python reactor migrate |
Apply pending database migrations. |
uv run python reactor migrate:status |
Inspect migration status. |
uv run python reactor schedule:list |
Inspect registered scheduled tasks. |
uv run python reactor schedule:work |
Run the scheduler. |
uv run python reactor test --start-dir=tests/orm --verbosity=1 |
Run a focused test suite in this checkout. |
uv run python reactor list |
Discover the available commands. |
Custom commands can use the same injected services as your HTTP controllers. Scheduled tasks run those command signatures through APScheduler.
Orionis builds on the ecosystem rather than asking you to leave it:
Granian for Rust-powered HTTP. msgspec for typed validation and encoding. SQLAlchemy Core for async database execution. Jinja2 for templates. APScheduler for scheduling. Rich for the terminal.
Orionis supplies the application architecture that connects them. Its request path uses precompiled routing and cached reflection metadata; actual throughput depends on your routes, middleware, database, and deployment.
The documentation website is still growing alongside the alpha. For implementation-level detail, start with these versioned guides:
| Guide | Explore |
|---|---|
| Container & DI | Providers, service lifetimes, scopes, and facades. |
| Validation | Schemas, field constraints, custom rules, and errors. |
| ORM & Database (Spanish) | Models, relationships, queries, migrations, and transactions. |
| Views | Async templates and their integration with forms and sessions. |
| Storage | Disks, uploads, streams, and cloud drivers. |
| Mailables, direct sends, MIME, storage attachments, SMTP, and .eml files. | |
| Testing | Async test cases, injected dependencies, and the runner. |
Most module guides also have a sibling README.es.md. Authentication code
lives in orionis/auth; the
project website introduces the framework.
Alpha. Open source. Actively evolving. Pin the version you evaluate and review changes before upgrading. Compatibility and production readiness should be assessed against your own application requirements.
Background tasks run in process, after a response; they are not a durable queue. Mail supports synchronous composition with asynchronous SMTP or file delivery. Durable queues, WebSockets, and Inertia/Vite integrations are not implemented yet. Authentication currently covers sessions and personal access tokens, not JWT, OAuth, MFA, or password-reset flows.
The most useful contribution is a real use case. Build a small feature, bring a reproducible bug, improve an example, or tell us which API feels awkward. You do not need to know the whole framework to improve one part of it.
- Report a bug or request a feature.
- Ask questions and share what you are building.
- Submit focused pull requests with relevant tests. Check the touched module with Ruff and run its tests through Reactor; see the test workflow for the current CI commands.
- Send security-sensitive reports privately to raulmauriciounate@gmail.com.
Created and maintained by Raul Mauricio Uñate Castro. Released under the MIT license.
The next part of your application already has a home.
