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.

Download files

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

Source Distribution

fastapi_ronin-1.0.1.tar.gz (571.2 kB view details)

Uploaded Source

Built Distribution

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

fastapi_ronin-1.0.1-py3-none-any.whl (30.5 kB view details)

Uploaded Python 3

File details

Details for the file fastapi_ronin-1.0.1.tar.gz.

File metadata

  • Download URL: fastapi_ronin-1.0.1.tar.gz
  • Upload date:
  • Size: 571.2 kB
  • Tags: Source
  • Uploaded using 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}

File hashes

Hashes for fastapi_ronin-1.0.1.tar.gz
Algorithm Hash digest
SHA256 981d7981583d22a562663f67826e2176b77a7f9eda82ebf82138266443400a7b
MD5 5e2ff689de2072857f2d5b9784638d8b
BLAKE2b-256 85eba0e0e2c5ea78353d5716a1c9c3c6df774ad737de8ef303e53fa2a4d6c270

See more details on using hashes here.

File details

Details for the file fastapi_ronin-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: fastapi_ronin-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 30.5 kB
  • Tags: Python 3
  • Uploaded using 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}

File hashes

Hashes for fastapi_ronin-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 b8bd25c3851c4ef4b86ab95b839026cec3485653b90e46298fdadd56422a72f2
MD5 ea2e1e79fc257d70cc38b3a1145b1621
BLAKE2b-256 877817c66e6b99cc4a7137999c8dbb54f7c77e49b12f0f148ad5002b72632fe9

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

1.0.1 This release

2 files

1.0.0

2 files

0.5.0

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

0.1.0

2 files

Supported by

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