NestJS concepts and their Austial equivalents
Austial keeps NestJS's vocabulary where the idea translates, so the mental model you already have still works. The table maps the pieces you use every day.
| NestJS | Austial |
|---|---|
| @Module({ imports, controllers, providers, exports }) | @Module(imports=, controllers=, providers=, exports=) |
| @Controller() with @Get(), @Post() | @Controller(prefix) with @Get, @Post, @Put, @Patch, @Delete |
| @Injectable() and constructor injection | @Injectable() and __init__ type hints, resolved recursively |
| Guards and CanActivate | CanActivate and @UseGuards |
| Pipes and ValidationPipe | PipeTransform and a Pydantic-backed ValidationPipe |
| Interceptors | NestInterceptor and @UseInterceptors |
| Exception filters and @Catch() | ExceptionFilter and @Catch |
| Middleware and configure(consumer) | NestMiddleware and configure(consumer) |
| ConfigModule and ConfigService | ConfigModule.for_root() and ConfigService |
| Test.createTestingModule() | Test.create_testing_module() |
| nest new, nest generate | austial new, austial generate |
A controller you will recognise
Decorators read top to bottom in the order they execute: the guard runs first, then the interceptor wraps the handler. Path parameters use the familiar :id syntax, translated to FastAPI's {id} under the hood.
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):
...What is different in Python
Austial is deliberately not a 1:1 port. A few differences are worth knowing before you start.
- File names use underscores (cats_controller.py), because Python cannot import a file called cats.controller.py.
- DTOs are plain Pydantic models, and type hints do the work that reflect-metadata does in TypeScript.
- Interceptors wrap the handler with a plain async continuation, not an RxJS stream.
- Providers are singletons by default, with a transient scope. There is no request scope yet.
- Module imports and exports are documentation today: all providers register into one application-wide container.
A full CRUD resource in one command
NestJS developers expect nest generate resource to produce a module, controller, service and DTOs. austial generate resource cats does the same and patches app_module.py for you. The controller it scaffolds looks like this.
from austial import Body, Controller, Delete, Get, Patch, Post
@Controller("cats")
class CatController:
def __init__(self, service: CatService):
self.service = service
@Post()
async def create(self, dto: CreateCatDto = Body()) -> Cat:
return self.service.create(dto)
@Get()
async def find_all(self) -> list[Cat]:
return self.service.find_all()
@Get(":id")
async def find_one(self, id: int) -> Cat:
return self.service.find_one(id)
@Patch(":id")
async def update(self, id: int, dto: UpdateCatDto = Body()) -> Cat:
return self.service.update(id, dto)
@Delete(":id")
async def remove(self, id: int) -> dict:
self.service.remove(id)
return {"deleted": True}Bootstrapping, like NestFactory
Every app boots from src/main.py. AustialFactory.create walks the module import graph from AppModule, registers every provider and controller into one container, wires middleware declared in configure(), and returns an application that wraps a real FastAPI instance. Because it is ASGI-callable, uvicorn src.main:app works directly.
from austial import AustialFactory
from austial.common.exceptions import AllExceptionsFilter
from austial.common.pipes import ValidationPipe
from .app_module import AppModule
def _build():
app = AustialFactory.create(AppModule, title="my-app")
app.use_global_pipes(ValidationPipe())
app.use_global_filters(AllExceptionsFilter())
app.enable_cors()
return app
app = _build()Errors you already know how to read
Built-in exceptions such as NotFoundException, BadRequestException and ForbiddenException turn into the same JSON shape NestJS clients expect, with statusCode, message and error fields, plus a timestamp and the request path. Validation errors from FastAPI itself and any other uncaught exception go through the same filter chain, so a forgotten try/except still produces a consistent body instead of a traceback.
Frequently asked questions
Is Austial a port of NestJS?
No. Austial is inspired by NestJS and borrows its vocabulary, but it is a Python-native framework built on FastAPI. Some NestJS features are intentionally out of scope for now, such as request-scoped providers and RxJS-style interceptors.
Does Austial run on FastAPI?
Yes. Every route is registered as a real FastAPI APIRouter, so FastAPI's generated OpenAPI documentation and Swagger UI at /docs keep working.
Which Python versions does Austial support?
Austial requires Python 3.11 or newer and uses uv for dependency management.