Skip to content

Getting Started

Meet arvel

arvel is an async-first, type-safe web framework for Python — with the batteries most apps need already wired: routing, an ORM, validation, views, queues, cache, mail, storage, and a full test kit. You opt into capabilities; you don't assemble them.

Three ideas shape everything:

  • A light core, lazy engines. import arvel is fast. Every heavy capability (HTTP, database, queue, …) lives behind an extra and imports its engine lazily, so your install and cold start carry only what you use.
  • Real engines underneath. Routes compile onto Litestar, the ORM speaks SQLAlchemy Core, views render through Jinja2. You write the ergonomic API and keep an inspectable, standard ASGI app — OpenAPI included.
  • One application object. A request, a queued job, and a CLI command all run against the same booted application, with the same container, config, and providers.

arvel targets Python 3.14+. Examples use uv, but pip works.

Creating an application

With the installer

Install the arvel CLI once, globally, then scaffold projects with it:

uv tool install arvel
arvel new blog

arvel new generates a complete, runnable app — config, routes, migrations, tests — and finishes the chores a fresh app forgets: it copies .env.example to a live .env and generates your APP_KEY, so encryption (cookies, encrypted casts) works from the first boot. Then:

cd blog
uv sync                        # install the app's own dependencies
source .venv/bin/activate
arvel serve --reload           # http://127.0.0.1:8000

The global CLI is for new

Inside a project, always use the project's own arvel (the one uv sync put in .venv). The globally-installed tool doesn't carry your app's dependencies, so project commands run from it will fail to import your app.

Pick an app shape with --profile, and add a working bearer-token auth flow with --auth:

arvel new blog --profile web          # api (default) | web | inertia-vue | minimal
arvel new api --auth                  # login route + token-protected endpoint + tests

Manually

Prefer to grow a project by hand? Add arvel to any package:

uv add 'arvel[standard]'       # the usual web stack: http, server, console, sqlite, view
uv add arvel                   # or: just the light core (CLI, container, helpers)

Capabilities are extras — add them as you need them; each capability's docs page names its extra:

uv add 'arvel[postgres]'       # the ORM on Postgres (SQLAlchemy + asyncpg)
uv add 'arvel[queue]'          # background jobs (taskiq)
uv add 'arvel[mail]'           # mail

Initial configuration

All of an app's configuration lives in config/ — plain Python modules, one per section (app.py, database.py, session.py, …), each reading the environment with sane defaults:

# config/app.py
from arvel import env

config = {
    "name": env("APP_NAME", "arvel"),
    "key": env("APP_KEY", ""),      # generated for you by `arvel new`
    "env": env("APP_ENV", "local"),
    "debug": env("APP_DEBUG", False),
    "timezone": "UTC",
    # … the scaffold's file also carries description/version
}

Environment-specific values belong in .env (never committed — the scaffold's .gitignore already excludes it); .env.example documents what your app expects. If you ever need to rotate the key: arvel key:generate.

Databases & migrations

A fresh app is configured for SQLite out of the box — zero setup, a file at database/database.sqlite:

arvel migrate                  # create the tables (the scaffold ships migrations)

To move to Postgres, add the extra, point DATABASE_URL at your server, and — because the fresh scaffold defines a single connection named sqlite — either just override its URL, or (clearer) add a named connection in config/database.py and select it:

uv add 'arvel[postgres]'
# config/database.py
config = {
    "default": env("DB_CONNECTION", "sqlite"),
    "connections": {
        "sqlite": {"url": env("DATABASE_URL", "sqlite+aiosqlite:///database/database.sqlite")},
        "pgsql":  {"url": env("DATABASE_URL", "")},
    },
}
# .env
DB_CONNECTION=pgsql
DATABASE_URL=postgresql+asyncpg://user:pass@localhost/blog

See Database for connections, and Migrations for the schema builder.

The dev server

arvel serve --reload

Your app is a standard ASGI application (asgi.py exposes it). arvel serve runs it on granian (shipped with arvel[server]) in both dev and production; because it's plain ASGI, any other ASGI server works too if you install one. Interactive API docs are served at /schema (raw document at /schema/openapi.json) from the OpenAPI your routes generate — the path is config/openapi.py's to change.

Two more commands you'll use daily:

arvel route:list               # every registered route, with names + middleware
arvel shell                    # an IPython REPL with your app booted and models loaded

Next steps

Depends on what you're building:

Every capability page opens with the problem it solves before the API — read them in any order.