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.
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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7620d06173350538a4c025000a96eaf790be197436468436d4b10595db72f6a7
|
|
| MD5 |
46ecc86266092f34032ffbe73b9f7857
|
|
| BLAKE2b-256 |
83ccc0aabf14f4255a8e7e0ed6097e49362fe7eda1a15332381f5305c9d312f9
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fbd14992ebc9253d91df0eabe1b58ddca98bfcb341a2c06765ad8a3bba937ae4
|
|
| MD5 |
c22b52d66b69736155ea7dfac062364b
|
|
| BLAKE2b-256 |
b64e012aca94060981e111fe109e0b014cd6f948678a4ddbc71edae662b5c6a3
|