FastAPI Ronin
FastAPI Ronin
Build REST APIs with Django REST Framework patterns in FastAPI
Transform your FastAPI development with familiar Django REST Framework patterns.
FastAPI Ronin gives FastAPI + Tortoise ORM projects a clean class-based API layer: ViewSets, explicit schemas, filters, pagination, permissions, response wrappers, custom actions, request state, and cache.
It is small enough to understand quickly, but structured enough to grow from a single-file prototype into a domain-oriented production app.
📦 Installation
uv add fastapi-ronin fastapi tortoise-orm uvicorn
For Redis-backed cache:
uv add "fastapi-ronin[redis]"
🚀 Complete App in One File
The root main.py is a complete runnable application. It includes
database setup, a model, explicit schemas, filters, ordering, pagination,
response wrappers, cache, and a custom action.
from contextlib import asynccontextmanager
from datetime import datetime
from fastapi import APIRouter, FastAPI
from pydantic import BaseModel
from tortoise import fields
from tortoise.contrib.fastapi import register_tortoise
from tortoise.contrib.pydantic import PydanticModel
from tortoise.expressions import Q
from tortoise.models import Model
from tortoise.queryset import QuerySet
from fastapi_ronin.cache import cache
from fastapi_ronin.decorators import action, schema, viewset
from fastapi_ronin.filters import CharFilter, DateTimeFilter, FilterSet, OrderingFilter, Parameter
from fastapi_ronin.pagination import PageNumberPagination
from fastapi_ronin.viewsets import ModelViewSet
from fastapi_ronin.wrappers import PaginatedResponseDataWrapper, ResponseDataWrapper
def register_database(app: FastAPI):
register_tortoise(
app,
db_url='sqlite://db.sqlite3',
modules={'models': ['main']},
generate_schemas=True,
add_exception_handlers=True,
)
class Company(Model):
id = fields.IntField(primary_key=True)
name = fields.CharField(max_length=255)
full_name = fields.TextField(null=True)
created_at = fields.DatetimeField(auto_now_add=True)
updated_at = fields.DatetimeField(auto_now=True)
@schema(Company)
class CompanyCreateSchema(PydanticModel):
name: str
full_name: str | None
@schema(Company)
class CompanyReadSchema(CompanyCreateSchema):
id: int
created_at: datetime
updated_at: datetime
class StatsSchema(BaseModel):
total: int
called_cache: int = 0
class CompanyFilterSet(FilterSet):
fields = [
CharFilter(field_name='name', view_name='search_by_name', lookup_expr='icontains'),
CharFilter(field_name='search', method='filter_by_search'),
DateTimeFilter(field_name='created_at', lookups=['gte', 'lte', 'exact']),
DateTimeFilter(field_name='updated_at', lookups=['gte', 'lte', 'exact']),
]
ordering = OrderingFilter(
fields=(
'name',
('created', 'created_at'),
('updated', 'updated_at'),
),
default=('-created',),
)
def filter_by_search(self, queryset: QuerySet[Company], value: str, parameter: Parameter):
return queryset.filter(Q(name__icontains=value) | Q(full_name__icontains=value))
class Meta:
model = Company
router = APIRouter(prefix='/companies', tags=['companies'])
@viewset(router)
class CompanyViewSet(ModelViewSet[Company]):
model = Company
create_schema = CompanyCreateSchema
read_schema = CompanyReadSchema
pagination = PageNumberPagination
list_wrapper = PaginatedResponseDataWrapper
single_wrapper = ResponseDataWrapper
filterset_class = CompanyFilterSet
@action(methods=['GET'], detail=False)
async def stats(self) -> StatsSchema:
called = (await cache.get('stats:call') or 0) + 1
await cache.set('stats:call', called)
return StatsSchema(total=await Company.all().count(), called_cache=called)
@asynccontextmanager
async def lifespan(app: FastAPI):
await cache.init(None)
yield
await cache.close()
app = FastAPI(title='My API', lifespan=lifespan)
register_database(app)
app.include_router(router)
Start server:
uvicorn main:app --reload
Open http://127.0.0.1:8000/docs.
📋 What You Get
This creates the following endpoints:
GET /companies/- list companies with filters, ordering, pagination, and wrapper metadataPOST /companies/- create a new companyGET /companies/{item_id}/- retrieve a companyPUT /companies/{item_id}/- update a companyPATCH /companies/{item_id}/- partially update a companyDELETE /companies/{item_id}/- delete a companyGET /companies/stats/- custom cached stats endpoint
Example requests:
GET /companies/?search=acme
GET /companies/?search_by_name=corp
GET /companies/?created_at__gte=2026-01-01T00:00:00
GET /companies/?ordering=-updated
Example list response:
{
"data": [
{
"id": 1,
"name": "Acme Corp",
"full_name": "Acme Corporation Ltd.",
"created_at": "2026-01-01T10:00:00Z",
"updated_at": "2026-01-01T10:00:00Z"
}
],
"meta": {
"page": 1,
"size": 10,
"total": 47,
"pages": 5
}
}
Example detail response:
{
"data": {
"id": 1,
"name": "Acme Corp",
"full_name": "Acme Corporation Ltd.",
"created_at": "2026-01-01T10:00:00Z",
"updated_at": "2026-01-01T10:00:00Z"
}
}
Example custom action response:
{
"total": 123,
"called_cache": 4
}
✨ Key Features
🎯 ViewSets
Django-like ViewSets with automatic CRUD routes and custom actions. Keep API behavior close to the resource it belongs to.📋 Explicit Schemas
Write normal Pydantic models and bind them to Tortoise ORM models with@schema(Model). Your API contract stays visible in code.
🔍 Filters & Ordering
Expose typed FastAPI query parameters and turn them into Tortoise queryset filters.📄 Pagination
Use page-number or limit-offset pagination with response metadata.🔄 Response Wrappers
Standardize response shapes for list, detail, create, and update endpoints.🔒 Permissions & State
Use request-scoped state and permission classes for authentication-aware APIs.⚡ Cache
Use in-memory cache locally and Redis-backed cache when you need shared storage.🎯 Philosophy
FastAPI Ronin is designed with these principles:
- Familiar: If you know Django REST Framework, ViewSets and permissions will feel natural.
- Explicit: Schemas are real Python classes, not hidden dynamic output.
- Flexible: Use only the pieces you need: ViewSets, filters, wrappers, permissions, cache, or state.
- Fast: Built on FastAPI, async Python, and Tortoise ORM.
- Modular: Start in one file, then move to a domain architecture when the app grows.
📚 Getting Started
Ready to build a scalable project layout? Start with the Quick Start guide.
Want to dive deeper?
- ViewSets - core ViewSet concepts
- Schemas - explicit request and response models
- Filters - query parameters and lookup expressions
- Cache - in-memory and Redis-backed cache
- Permissions - authentication-aware access rules
- Pagination - page-number and limit-offset pagination
- State Management - request-scoped state
- Response Wrappers - consistent API responses
🤝 Community
FastAPI Ronin is open source. Issues, ideas, documentation improvements, and pull requests all help shape the library.
- GitHub: github.com/bubaley/fastapi-ronin
- Issues: Report bugs or request features
- Discussions: Ask questions and share patterns
📄 License
FastAPI Ronin is released under the MIT License.
Metadata
Release files for fastapi-ronin 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fastapi_ronin-1.0.1.tar.gz | 571.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fastapi_ronin-1.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 601.8 kB
Release files / fastapi_ronin-1.0.1.tar.gz
| Download URL | fastapi_ronin-1.0.1.tar.gz |
|---|---|
| Size | 571.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
981d7981583d22a562663f67826e2176b77a7f9eda82ebf82138266443400a7b
|
|
BLAKE2b-256 checksum How to use checksums |
85eba0e0e2c5ea78353d5716a1c9c3c6df774ad737de8ef303e53fa2a4d6c270
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / fastapi_ronin-1.0.1-py3-none-any.whl
| Download URL | fastapi_ronin-1.0.1-py3-none-any.whl |
|---|---|
| Size | 30.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b8bd25c3851c4ef4b86ab95b839026cec3485653b90e46298fdadd56422a72f2
|
|
BLAKE2b-256 checksum How to use checksums |
877817c66e6b99cc4a7137999c8dbb54f7c77e49b12f0f148ad5002b72632fe9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|