( NestJS developers, start here )

NestJS for Python, built on FastAPI

Austial is a Python web framework inspired by NestJS and built on top of FastAPI. If you know the NestJS way of organising a backend, the concepts carry over almost one to one. The syntax is Python: decorators, type hints and Pydantic.

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 concepts and their Austial equivalents
NestJSAustial
@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 CanActivateCanActivate and @UseGuards
Pipes and ValidationPipePipeTransform and a Pydantic-backed ValidationPipe
InterceptorsNestInterceptor and @UseInterceptors
Exception filters and @Catch()ExceptionFilter and @Catch
Middleware and configure(consumer)NestMiddleware and configure(consumer)
ConfigModule and ConfigServiceConfigModule.for_root() and ConfigService
Test.createTestingModule()Test.create_testing_module()
nest new, nest generateaustial 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.

Go deeper in the docs

( Keep reading )

( Click. Copy. Build. )

Seeing “command not found”? Run uv tool update-shell, then open a new terminal. Details