Skip to content

Directory Structure

Introduction

The default Arvel application structure is intended to provide a great starting point for both large and small applications. arvel new generates a Laravel-shaped layout — here's what each directory holds and how the app boots.

The Generated Layout

my-app/
├── app/
│   ├── console/
│   │   └── commands/         # custom CLI commands
│   ├── http/
│   │   ├── controllers/      # controller classes
│   │   ├── middleware/       # HTTP middleware
│   │   ├── requests/         # form requests (validation)
│   │   └── resources/        # JSON resource transformers
│   ├── models/               # ORM models
│   └── providers/            # your service providers
├── bootstrap/
│   ├── app.py                # builds the Application
│   └── providers.py          # your provider list
├── config/
│   ├── app.py                # app name, env, debug, url, timezone, locale
│   ├── database.py
│   ├── logging.py
│   └── view.py
├── database/
│   ├── factories/            # model factories
│   ├── migrations/           # schema migrations
│   └── seeders/              # seed data
├── public/
│   └── asgi.py               # ASGI entrypoint (public.asgi:asgi)
├── routes/
│   ├── api.py                # JSON API routes
│   ├── web.py                # web routes
│   └── console.py            # scheduled tasks / console routing
├── storage/                  # cache, sessions, views, logs, uploads
├── tests/
│   ├── feature/
│   └── unit/
└── .env                      # local environment variables

How the App Boots

bootstrap/app.py assembles the application with a fluent builder:

from arvel import Application

_BASE_PATH = ...  # project root


def create_application() -> Application:
    routes_dir = _BASE_PATH / "routes"
    return (
        Application.configure(_BASE_PATH)
        .with_config_dir(_BASE_PATH / "config")
        .with_providers(_BASE_PATH / "bootstrap" / "providers.py")
        .with_routing(
            web=routes_dir / "web.py",
            api=routes_dir / "api.py",
            console=routes_dir / "console.py",
        )
        .create()
    )

public/asgi.py turns that into the ASGI app the server runs:

from bootstrap.app import create_application

asgi = create_application().into_asgi()

arvel serve runs public.asgi:asgi under uvicorn. See Application lifecycle for the full boot sequence.

Where Things Go

You want to… Put it in… Generate with
Add a JSON endpoint routes/api.py
Add a model app/models/ arvel make:model
Validate a request body app/http/requests/ arvel make:request
Transform a response app/http/resources/ arvel make:resource
Add business logic app/services/ arvel make:service
Register bindings / boot logic app/providers/ arvel make:provider
Add a background job app/jobs/ arvel make:job
Add a scheduled task routes/console.py

Note

The skeleton only scaffolds app/console/, app/http/, app/models/, and app/providers/. Directories like app/services/ and app/jobs/ don't exist until the matching make:* command creates them — it creates the directory for you.

Configuration Files

config/*.py modules expose plain module-level attributes, read at runtime via dotted keys:

# config/app.py
name: str = _env("APP_NAME", "MyApp")
env: str = _env("APP_ENV", "production").lower()
debug: bool = _env("APP_DEBUG", default=False)
from arvel.config import config

app_name = config("app.name")          # reads config/app.py → name
debug = config("app.debug", False)     # never raises; returns default if missing

Config keys are lowercase and match the module attribute names. See Configuration for both the dotted-key style above and the typed ArvelSettings style.

Environment

Local environment lives in .env, loaded automatically on boot — it never overrides values already in the process environment. The generated .env starts with SQLite and commented-out Postgres/Redis blocks:

APP_NAME=MyApp
APP_ENV=production
APP_DEBUG=false
APP_URL=http://localhost:8000
APP_TIMEZONE=UTC

LOG_LEVEL=info

DB_CONNECTION=sqlite
# DB_URL=sqlite+aiosqlite:///database/database.sqlite

Warning

Never commit secrets. Keep .env out of version control and generate APP_KEY per environment with arvel key:generate.