Skip to main content

FastAPI Ronin

FastAPI Ronin

FastAPI Ronin - Django REST Framework patterns for FastAPI

Build REST APIs with Django REST Framework patterns in FastAPI

Version Python versions License


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.

🚀 Get Started in 5 Minutes

📦 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 metadata
  • POST /companies/ - create a new company
  • GET /companies/{item_id}/ - retrieve a company
  • PUT /companies/{item_id}/ - update a company
  • PATCH /companies/{item_id}/ - partially update a company
  • DELETE /companies/{item_id}/ - delete a company
  • GET /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?

🤝 Community

FastAPI Ronin is open source. Issues, ideas, documentation improvements, and pull requests all help shape the library.

📄 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)

Source distribution for fastapi-ronin 1.0.1
File Size Uploaded
fastapi_ronin-1.0.1.tar.gz 571.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fastapi-ronin 1.0.1
File Interpreter ABI Platform
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}

Release history Release notifications | RSS feed

This release

1.0.1 This release

2 release files

1.0.0

2 release files

0.5.0

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page