Overview — Aquilia Documentation
Comprehensive guide and documentation for Overview in the Aquilia framework. View API reference, examples, and implementation patterns.
Tutorials Overview Understanding the Aquilia Application Architecture and Scaffolded Project Layout Welcome to the Aquilia step-by-step tutorials! Aquilia is a high-performance, modular, and manifest-driven ASGI web framework built for Python 3.12+. Before we dive into writing code, let's understand the architectural principles, how to scaffold a new application, and what files are created under the hood. Core Architectural Pillars Manifest-First Topology Topologies and component registries are declared explicitly in Python manifests ( manifest.py ). This guarantees zero implicit class scanning or magic imports during start-up. Contract-Based APIs Input and output structures are validated using Contracts . Contracts act as the single source of truth for validation, serialization, database imprinting, and OpenAPI schema generation. Dependency Injection An async-first DI Container resolves class dependencies, managing lifecycle scopes, validating cross-module boundaries, and preventing circular dependencies. Scaffolding Your First Workspace Aquilia projects are organized inside a Workspace. A workspace defines the shared environment settings, database connections, global middleware, and enabled integrations. To initialize a new workspace, use the aq init workspace command: This scaffolds a production-grade directory layout. Let's inspect the files it generates: Key Scaffolded Files Explained Let's view the exact contents of the primary files generated by the CLI, highlighting how they orchestrate the application lifecycle. workspace.py This is the root configuration file loaded by the aq run server. It defines environment profiles (BaseEnv, DevEnv, ProdEnv) and registers core integrations like the database ORM, dependency injection, caching, and templates. Env → environment variables """ from aquilia import Workspace, Module from aquilia import AquilaConfig, Secret, Env from aquilia.integrations import ( MiddlewareChain, DiIntegration, RegistryIntegration, RoutingIntegration, FaultHandlingIntegration, PatternsIntegration, DatabaseIntegration, CacheIntegration, TemplatesIntegration, StaticFilesIntegration, ) # ── Environment Configuration ──────────────────────────────────── # Operational settings (server, auth, DB, mail) as Python classes. # Activate: AQ_ENV=dev (default) | AQ_ENV=prod class BaseEnv(AquilaConfig): """Shared defaults — every environment inherits these.""" env = "dev" class server(AquilaConfig.Server): host = "127.0.0.1" port = 8000 workers = 1 reload = False # ── Timeouts ─────────────────────────────────────────── # timeout_keep_alive = 5 # seconds to keep idle connections open # timeout_worker_healthcheck = 30 # seconds before worker considered unresponsive # timeout_graceful_shutdown = 30 # seconds to wait on shutdown # ── Limits ───────────────────────────────────────────── # backlog = 2048 # TCP connection backlog # limit_concurrency = None # max concurrent connections # limit_max_requests = None # restart worker after N requests # ── Proxy / Headers ─────────────────────────────────── # proxy_headers = True # trust X-Forwarded-* headers # forwarded_allow_ips = "*" # IPs allowed to set proxy headers # root_path = "" # ASGI root_path for reverse proxies # ── WebSocket ───────────────────────────────────────── # ws_max_size = 16_777_216 # max WebSocket message (16 MiB) # ws_ping_interval = 20.0 # ping every N seconds # ws_ping_timeout = 20.0 # close if pong not received # ── TLS / SSL ───────────────────────────────────────── # ssl_certfile = "/etc/certs/cert.pem" # ssl_keyfile = "/etc/certs/key.pem" # ssl_ca_certs = None # ── Protocol Implementation ─────────────────────────── # http = "auto" # "auto" | "h11" | "httptools" # ws = "auto" # "auto" | "wsproto" | "websockets" | "none" # loop = "auto" # "auto" | "asyncio" | "uvloop" class auth(AquilaConfig.Auth): secret_key = Secret(env="AQ_SECRET_KEY", default="change-me-in-prod") password_hasher = AquilaConfig.PasswordHasher(algorithm="argon2id") class DevEnv(BaseEnv): """Development — hot-reload, debug pages, single worker.""" env = "dev" class server(BaseEnv.server): debug = True reload = True workers = 1 class ProdEnv(BaseEnv): """Production — multi-worker, no reload, no debug.""" env = "prod" class server(BaseEnv.server): host = Env("AQ_HOST", default="0.0.0.0") port = Env("AQ_PORT", default=8000, cast=int) workers = Env("AQ_WORKERS", default=4, cast=int) reload = False access_log = False timeout_keep_alive = 30 limit_max_requests = 10_000 # auto-restart workers after 10k requests proxy_headers = True # trust X-Forwarded-* from load balancer class auth(BaseEnv.auth): secret_key = Secret(env="AQ_SECRET_KEY", required=True) # ── Workspace Structure ────────────────────────────────────────── workspace = ( Workspace( name="test-space", version="1.0.0", description="Aquilia workspace", ) # Wire environment config (resolved by AQ_ENV at runtime) .env_config(BaseEnv) # Starter module -- registered here so the server does not need # to hard-code it. Delete this line (and starter.py) once you # add your own modules with a GET "/" route. .starter("starter") # Add modules here with explicit configuration: # .module(Module("auth", version="1.0.0", description="Authentication module").route_prefix("/api/v1/auth").depends_on("core")) # .module(Module("users", version="1.0.0", description="User management").route_prefix("/api/v1/users").depends_on("auth", "core")) # Middleware chain -- controls which middleware runs and in what order. # Presets: defaults() (dev), production(), minimal() # Custom: MiddlewareChain.chain().use("aquilia.middleware.ExceptionMiddleware", priority=1).use(...) .middleware(MiddlewareChain.defaults()) # Integrations - Configure core systems .integrate(DiIntegration(auto_wire=True)) .integrate(RegistryIntegration()) .integrate(RoutingIntegration(strict_matching=True)) .integrate(FaultHandlingIntegration(default_strategy="propagate")) .integrate(PatternsIntegration()) # Database - Configure the ORM backend .integrate(DatabaseIntegration( url="sqlite:///db.sqlite3", # SQLite (dev) # url="postgresql://user:pass@localhost:5432/test-space", # PostgreSQL pool_size=5, echo=False, auto_migrate=False, )) # Cache - In-memory by default, switch to Redis for production .integrate(CacheIntegration( backend="memory", default_ttl=300, max_size=1024, key_prefix="test-space:", )) # Templates - Fluent configuration .integrate( TemplatesIntegration.builder() .source("templates") .scan_modules() .cached("memory") .secure() ) # Static Files - Serve static assets (CSS, JS, images) .integrate(StaticFilesIntegration( directories= , cache_max_age=86400, etag=True, )) # Sessions (uncomment to enable session management) # .sessions( # policies=[ # SessionPolicy( # name="default", # ttl=timedelta(days=7), # idle_timeout=timedelta(hours=1), # absolute_timeout=timedelta(days=30), # rotate_on_use=False, # rotate_on_privilege_change=True, # fingerprint_binding=False, # scope="user", # persistence=PersistencePolicy( # enabled=True, # store_name="default", # write_through=True, # compress=False, # ), # concurrency=ConcurrencyPolicy( # max_sessions_per_principal=5, # behavior_on_limit="evict_oldest", # ), # transport=TransportPolicy( # cookie_name="test-space_session", # cookie_secure=False, # cookie_httponly=True, # cookie_samesite="lax", # ), # ), # ], # ) # Security (uncomment to enable security middleware) # Fine-grained: use Integration.cors(), Integration.csp(), # Integration.rate_limit() with .integrate(). # .security( # cors_enabled=False, # csrf_protection=False, # helmet_enabled=True, # rate_limiting=False, # ) # Telemetry (uncomment to enable observability) # .telemetry( # tracing_enabled=False, # metrics_enabled=True, # logging_enabled=True, # ) # Admin Dashboard (uncomment to enable admin at /admin/) # Requires: aq admin createsuperuser # .integrate(AdminIntegration( # url_prefix="/admin", # site_title="test-space Admin", # auto_discover=True, # )) )`} language="python" filename="workspace.py" highlightLines= /> starter.py The starter script provides a default welcome endpoint when the server starts up. In production or once custom modules are created, the .starter("starter") pointer is removed from workspace.py, and this file is deleted. Scaffolding Modules with aq add module Aquilia's CLI features a complete code-generation engine to scaffold self-contained application modules. Running aq add module prompts you interactively, but you can also configure options directly using command line arguments. Command Arguments and Options , , , , , , , ].map((opt) => ( ))} Scaffolded Module Directory Layout Executing aq add module generates the following files inside your module directory: / ├── __init__.py ├── manifest.py # Module manifest (single source of truth for dependencies and components) ├── controllers.py # Class-based route handler controllers (e.g. GET, POST endpoints) ├── services.py # Dependency injected services containing business logic ├── models.py # Database ORM models mapping to SQL tables ├── contracts.py # Request/response validation contract contracts using Facets └── faults.py # Module-specific structured domain exception faults`} language="text" /> Default manifest.py Content The manifest.py file acts as the registration hub for all components in the module. Here is the exact scaffolded template generated by the CLI: CLI Usage Examples Enabling the Admin Dashboard Aquilia includes a secure, built-in admin dashboard for user and permissions auditing, database records inspection, Docker container monitoring, and server telemetry. Let's look at how to enable it and set up your credentials. 1. Configure integrations in workspace.py The admin panel requires active session management, database connectivity, and static files configuration. By default, basic admin pages are visible, but advanced systems (monitoring, Kubernetes pods, SQL query profiling, task queues) are opt-in. To enable all administrative modules, you can use the fluent .enable_all() method or the class method AdminModules.all_enabled(): Understanding AdminModules Config Options The AdminModules config class lets you selectively enable or disable admin sections: , , , , , ].map((m) => ( ))} 2. Run Pre-flight Dependency Checks Use the CLI's aq admin check command to statically verify that all dependencies are enabled: 3. Database Migrations (Two-Step Flow) Before creating a superuser, the database tables must exist. Important: You must always run the migrations in two separate steps: Step A: Generate Migration Files (aq db makemigrations) This command inspects all ORM models declared in your module manifests, compares them to database schema snapshots, and generates python migration scripts inside each module's migrations/ directory. Step B: Run Migration (aq db migrate) This command reads the generated migration files, applies table mutations to your database, and registers them in the history logs. 4. Create an Admin Superuser Once migrations are completed, create a superuser using the CLI: Once created, run aq run, navigate to http://127.0.0.1:8000/admin, and log in to explore the dashboard. Running and Verifying the Workspace To spin up the local development server with hot-reloading active, run the following CLI command: The server will start at http://127.0.0.1:8000. Open your browser and navigate to this URL to view the default welcome page. Integrity Validation: You can run aq validate at any time to statically verify module manifests, route configurations, and DI provider chains. Begin Your Journey Now that we understand how a workspace is bootstrapped and what its core structure represents, let's build a real-world application! Click the link below to follow our complete step-by-step tutorial on building a CRUD-based Todo API with database storage. → Build a Todo Application (End-to-End Tutorial) )
Go to Homepage