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.
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 fastapi_ronin-1.0.0.tar.gz.
File metadata
- Download URL: fastapi_ronin-1.0.0.tar.gz
- Upload date:
- Size: 571.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d23b10fba976e4f6bac843a33ecb50a7ab8a14af5cc71c98fccc84f750cc6c03
|
|
| MD5 |
2c10f7a3b8fb23b5e673e0c2f151e0cd
|
|
| BLAKE2b-256 |
2b8eadfe71d7040b84a5568de88d631e50924e5118fb71c27be222ff27fb270a
|
File details
Details for the file fastapi_ronin-1.0.0-py3-none-any.whl.
File metadata
- Download URL: fastapi_ronin-1.0.0-py3-none-any.whl
- Upload date:
- Size: 30.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
90fff7b7af91f6194e9d5833672cba93d03be70e2599090e7737a87ae24fafbf
|
|
| MD5 |
73b50793d8627a17b11fd986ab622893
|
|
| BLAKE2b-256 |
35ef9afe81dc515ea49a959e1c0d20b6141642ebc3c4a48c6a7f62b37ae94296
|