Skip to main content

A NestJS-inspired framework for Python built on top of FastAPI

Project description

๐Ÿชบ FastNest

A NestJS-inspired framework for Python, built on FastAPI.

Bring the architecture you love from Node.js โ€” Modules, Controllers, Guards, Pipes, Interceptors, and Dependency Injection โ€” to the Python ecosystem.

Python FastAPI License: MIT Status


Why FastNest?

FastAPI is great โ€” but as your project grows, you end up inventing your own structure. FastNest gives you that structure out of the box, inspired by the battle-tested patterns of NestJS:

Feature FastAPI alone FastNest
Dependency Injection Manual @Injectable() + automatic
Modules โ€” @Module() with imports/exports
Guards Depends() @UseGuard() on method or controller
Pipes Depends() @UsePipe() with transform chain
Interceptors Middleware @UseInterceptor() before/after
Lifecycle hooks lifespan manually OnModuleInit, OnApplicationBootstrap

Installation

pip install fastnest

Or for development:

pip install "fastnest[dev]"

Quick Start

from fastnest.core.decorators import Module, Controller, Injectable, Get, Post
from fastnest.core.factory import create_app
from fastnest.core.params import Body, Param
from fastnest.common.interfaces import CanActivate
from fastnest.common.pipes import ValidationPipe
from pydantic import BaseModel
from fastapi import Request


# โ”€โ”€ DTO โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class CreateUserDto(BaseModel):
    name: str
    email: str


# โ”€โ”€ Guard โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class AuthGuard(CanActivate):
    def can_activate(self, request: Request) -> bool:
        return request.headers.get("Authorization", "").startswith("Bearer ")


# โ”€โ”€ Service โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
@Injectable()
class UsersService:
    def __init__(self):
        self._users = []

    def find_all(self):
        return self._users

    def create(self, dto: CreateUserDto):
        user = {"id": len(self._users) + 1, **dto.model_dump()}
        self._users.append(user)
        return user


# โ”€โ”€ Controller โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
@Controller("users")
class UsersController:
    def __init__(self, service: UsersService):
        self.service = service

    @Get()
    async def find_all(self, request: Request):
        return self.service.find_all()

    @Post()
    @UseGuard(AuthGuard)
    @UsePipe(ValidationPipe)
    async def create(self, body: CreateUserDto = Body()):
        return self.service.create(body)


# โ”€โ”€ Module โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
@Module(controllers=[UsersController], providers=[UsersService])
class AppModule:
    pass


# โ”€โ”€ Bootstrap โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
app = create_app(AppModule)
uvicorn main:app --reload

Core Concepts

Modules

Modules are the building blocks. Each feature lives in its own module.

@Module(
    imports=[DatabaseModule],      # import providers from other modules
    controllers=[UsersController],
    providers=[UsersService],
    exports=[UsersService],        # share with other modules
)
class UsersModule:
    pass

Dependency Injection

Mark a class with @Injectable() and FastNest injects it automatically based on type annotations.

@Injectable()
class UsersService:
    def __init__(self, db: DatabaseService):  # โ† injected automatically
        self.db = db

Guards

class AuthGuard(CanActivate):
    def can_activate(self, request: Request) -> bool:
        return bool(request.headers.get("Authorization"))

# Method-level
@Get()
@UseGuard(AuthGuard)
async def profile(self, request: Request): ...

# Controller-level (applies to all routes)
@UseGuard(AuthGuard)
@Controller("users")
class UsersController: ...

# Global (applies to entire app)
from fastnest.core.factory import add_global_guard
add_global_guard(AuthGuard)

Pipes

from fastnest.common.pipes import ValidationPipe, ParseIntPipe

@Post()
@UsePipe(ValidationPipe)
async def create(self, body: CreateUserDto = Body()): ...

@Get("{id}")
@UsePipe(ParseIntPipe)
async def find_one(self, id = Param("id")): ...

Interceptors

import time
from fastnest.common.interfaces import NestInterceptor

class LoggingInterceptor(NestInterceptor):
    def intercept_before(self, request: Request):
        request.state.start = time.time()

    def intercept_after(self, request: Request, response):
        elapsed = time.time() - request.state.start
        print(f"{request.method} {request.url.path} โ€” {elapsed:.3f}s")
        return response

Custom Param Decorators

from fastnest.common.decorators import createParamDecorator

CurrentUser = createParamDecorator(
    lambda data, request: request.state.user
)

@Get("me")
async def me(self, user=CurrentUser()):
    return user

Dynamic Modules

@Module({})
class ConfigModule:
    @classmethod
    def for_root(cls, config: dict):
        return DynamicModule(
            module=cls,
            providers=[ConfigService],
            exports=[ConfigService],
            is_global=True,
        )

# Usage
@Module(imports=[ConfigModule.for_root({"debug": True})])
class AppModule:
    pass

Lifecycle Hooks

from fastnest.common.lifecycle import OnModuleInit, OnModuleDestroy

@Injectable()
class DatabaseService(OnModuleInit, OnModuleDestroy):
    async def on_module_init(self):
        await self.connect()

    async def on_module_destroy(self):
        await self.disconnect()

Middleware

from fastnest.core.middleware import NestMiddleware

class CorsMiddleware(NestMiddleware):
    async def use(self, request, call_next):
        response = await call_next(request)
        response.headers["Access-Control-Allow-Origin"] = "*"
        return response

@Module({})
class AppModule:
    def configure(self, consumer):
        consumer.apply(CorsMiddleware).for_all()

Request Pipeline

Every request flows through this pipeline in order:

Request
   โ”‚
   โ–ผ
Middleware          (global, applied before routing)
   โ”‚
   โ–ผ
Guards              (Global โ†’ Controller โ†’ Method)
   โ”‚
   โ–ผ
Interceptors Before (Global โ†’ Controller โ†’ Method)
   โ”‚
   โ–ผ
Pipes               (transform & validate params)
   โ”‚
   โ–ผ
Handler             (your controller method)
   โ”‚
   โ–ผ
Interceptors After  (Method โ†’ Controller โ†’ Global, reversed)
   โ”‚
   โ–ผ
Exception Filters   (catch unhandled errors)
   โ”‚
   โ–ผ
Response

Built-in Exceptions

from fastnest.common.exceptions import (
    BadRequestException,        # 400
    UnauthorizedException,      # 401
    ForbiddenException,         # 403
    NotFoundException,          # 404
    ConflictException,          # 409
    UnprocessableEntityException, # 422
    InternalServerErrorException, # 500
)

def find_one(self, id: int):
    user = self.db.find(id)
    if not user:
        raise NotFoundException(f"User #{id} not found")
    return user

๐Ÿ›ฐ๏ธ Real-time with WebSockets (New!)

FastNest now supports real-time communication out of the box. You can create WebSocket gateways just as easily as HTTP controllers.
Example Gateway
Python

from fastnest.core import WebSocketGateway, OnGatewayConnection, OnGatewayDisconnect from fastapi import WebSocket

@WebSocketGateway("/chat") class ChatGateway(OnGatewayConnection, OnGatewayDisconnect):

async def on_connection(self, websocket: WebSocket):
    print(f"Client connected: {websocket.client}")

async def on_disconnect(self, websocket: WebSocket):
    print("Client disconnected")

@SubscribeMessage("message")
async def handle_message(self, client: WebSocket, data: any):
    await client.send_json({"event": "reply", "data": "Message received!"})

๐Ÿ› ๏ธ Quick Start
1. Installation
Bash

pip install fastnest

2. Create a Module
Python

from fastnest.core import Module
from .app.controller import AppController
from .app.service import AppService

@Module(
    controllers=[AppController],
    providers=[AppService]
)
class AppModule:
    pass

3. Bootstrap the App
Python

from fastnest.core import FastNestFactory
from .app_module import AppModule

app = FastNestFactory.create(AppModule)

๐Ÿ“‚ Project Structure

FastNest encourages a clean structure to keep your project maintainable:

    core/: The engine (DI, Module discovery, Factory).  

    common/: Shared decorators, guards, and pipes.  

    dist/: Ready-to-use distribution packages.  

๐Ÿค Contributing

We welcome contributions! Please check our CONTRIBUTING.md for guidelines on how to submit issues and pull requests.  
๐Ÿ“„ License

This project is licensed under the MIT License.

Roadmap

  • Microservices transport (Redis, RabbitMQ, TCP)
  • CLI generator (fastnest generate module users)
  • Testing utilities (TestingModule.create_testing_module())
  • Swagger / OpenAPI auto-generation from decorators

Contributing

Contributions are welcome! See CONTRIBUTING.md for guidelines.


License

MIT โ€” see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fastnest-0.1.1.tar.gz (34.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fastnest-0.1.1-py3-none-any.whl (28.0 kB view details)

Uploaded Python 3

File details

Details for the file fastnest-0.1.1.tar.gz.

File metadata

  • Download URL: fastnest-0.1.1.tar.gz
  • Upload date:
  • Size: 34.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.4

File hashes

Hashes for fastnest-0.1.1.tar.gz
Algorithm Hash digest
SHA256 7620d06173350538a4c025000a96eaf790be197436468436d4b10595db72f6a7
MD5 46ecc86266092f34032ffbe73b9f7857
BLAKE2b-256 83ccc0aabf14f4255a8e7e0ed6097e49362fe7eda1a15332381f5305c9d312f9

See more details on using hashes here.

File details

Details for the file fastnest-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: fastnest-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 28.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.4

File hashes

Hashes for fastnest-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 fbd14992ebc9253d91df0eabe1b58ddca98bfcb341a2c06765ad8a3bba937ae4
MD5 c22b52d66b69736155ea7dfac062364b
BLAKE2b-256 b64e012aca94060981e111fe109e0b014cd6f948678a4ddbc71edae662b5c6a3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page