Introduction

Austial is a production-ready web framework for Python, built on top of FastAPI and inspired by NestJS. It gives Python decorator-driven modules/controllers/providers, real constructor-based dependency injection, the full request lifecycle (guards, pipes, interceptors, exception filters, middleware), a config layer, a database layer, Terminus-style health checks, a testing module — and a CLI so you can scaffold new projects and generate artifacts.

FastAPI is a fantastic ASGI toolkit, but it doesn’t prescribe an application architecture. Austial is opinionated on top of FastAPI: modules own controllers and providers, providers get dependency-injected by type, and the request pipeline

guards -> pipes -> interceptors (wrapping) -> handler -> exception filters

is a first-class concept instead of something you hand-roll with Depends() everywhere.

The Austial request lifecycle in order: guards, pipes, interceptors, handler, then exception filters.

Why Austial#

Routing, guards, and interceptors are all declared with decorators that read top to bottom, in the order they execute:

from austial import Controller, Get, UseGuards, UseInterceptors
from austial.common.interceptors import TransformInterceptor
 
from .api_key_guard import ApiKeyGuard
 
 
@Controller("cats")
class CatsController:
    @Get(":id")
    @UseGuards(ApiKeyGuard)
    @UseInterceptors(TransformInterceptor())
    async def find_one(self, id: int):
        ...

Path params use a familiar :id syntax (translated to FastAPI’s {id} under the hood), so route strings stay short, and the decorator stack — guard, interceptor, handler — reads top to bottom in the order it executes.

Architecture at a glance#

  • Modules — @Module(imports=, controllers=, providers=, exports=) compose the application graph.
  • Controllers — @Controller(prefix) plus @Get/@Post/@Put/@Patch/@Delete/@Options/@Head declare routes.
  • Providers — @Injectable(scope=Scope.DEFAULT | Scope.TRANSIENT | Scope.REQUEST); the container resolves __init__ type hints recursively, singleton by default. Custom providers use provider dicts: {"provide": TOKEN, "useClass"/"useValue"/"useFactory"/"useExisting": ..., "inject": [...]}. ModuleRef, DiscoveryService, LazyModuleLoader and forward_ref cover runtime lookup and circular graphs.
  • Bootstrap — AustialFactory.create(AppModule) returns an AustialApplication; await app.listen(8000) starts it. AustialFactory.create_application_context(AppModule) builds the graph without HTTP. Providers can hook on_module_init … on_application_shutdown.
  • Guards — CanActivate ABC + ExecutionContext, applied with @UseGuards(...).
  • Interceptors — NestInterceptor ABC (intercept(context, call_next)), applied with @UseInterceptors(...).
  • Pipes — PipeTransform ABC, including ValidationPipe, the Parse*Pipe family, DefaultValuePipe and ParseFilePipe.
  • Exception filters — ExceptionFilter ABC + @Catch(...), with a default AllExceptionsFilter.
  • Middleware — NestMiddleware ABC, wired up via configure(consumer) on modules.
  • Config — ConfigModule.for_root(env_file=".env") / ConfigService.
  • Database — austial.orm, a from-scratch TypeORM-shaped ORM: @Entity(), Repository, QueryBuilder, wired up via OrmModule.for_root(...).
  • More building blocks — austial.websockets (gateways, rooms), austial.microservices (TCP/local transports, hybrid apps), austial.cqrs, austial.schedule (@Cron/@Interval/@Timeout and a runtime registry), austial.http (an injectable HTTP client), austial.openapi, austial.graphql (Strawberry), austial.mongo, austial.prisma, OAuth/OIDC social login in austial.auth, and @Render templates.
  • Cross-cutting techniques — austial.cache (CacheModule, CacheInterceptor, @CacheKey/@CacheTTL), austial.events (EventEmitter, @OnEvent with wildcards), a pluggable Logger/LoggerService, global APP_GUARD/APP_PIPE/APP_INTERCEPTOR/APP_FILTER providers, async providers, and testing overrides.
  • HTTP extras — API versioning, a global prefix, host routing, @Redirect, @Sse, StreamableFile, file-upload helpers, response serialization (@Exclude/@Expose) and mapped types (PartialType, …).
  • Health checks — austial.terminus.HealthCheckService + MemoryHealthIndicator, DatabaseHealthIndicator.
  • Testing — austial.testing.Test.create_testing_module(...).compile().
  • CLI — austial new <name> / austial generate|g module|controller|service|resource <name>.

A note on file names#

Some JavaScript frameworks name files with dots (health.controller.ts) because Node resolves modules by explicit relative path strings. Python’s import a.b instead resolves a as a package and b as a submodule — a literal file health.controller.py isn’t importable via a normal import statement. So Austial uses underscore suffixes instead: health_controller.py, health_service.py, health_module.py, health_dto.py. The directory layout is one folder per feature under src/app/modules/, next to the root module in src/app/, with shared code in src/shared/ and non-Python assets in src/templates/.

What’s not (yet) implemented#

Austial is deliberately opinionated but not a 1:1 port of any single framework. A few concepts are intentionally out of scope for now, so you don’t go looking for them:

  • Module visibility is opt-in — by default every module’s providers live in one flat, application-wide container; strict_modules=True enforces imports/exports (see Module Visibility).
  • Pluggable HTTP platform — Austial runs on FastAPI/Starlette only; there is no Express-or-Fastify-style adapter layer.
  • GraphQL beyond Strawberry — code-first schemas via Strawberry only; no schema-first mode or federation helpers (subscriptions work over WebSocket).
  • SQL ORMs beyond the built-in one — it supports PostgreSQL and SQLite; there is no TypeORM, Sequelize or MikroORM adapter. MongoDB has Mongoose-style models; Prisma is wired in at the connection level.
  • RxJS-style interceptor streams — CallHandler.handle() is a plain async continuation, not an Observable.

None of this blocks building a real API with the full request lifecycle — it’s just worth knowing up front.

Where to go next#