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.0.tar.gz (571.1 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.0-py3-none-any.whl (30.5 kB view details)

Uploaded Python 3

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

Hashes for fastapi_ronin-1.0.0.tar.gz
Algorithm Hash digest
SHA256 d23b10fba976e4f6bac843a33ecb50a7ab8a14af5cc71c98fccc84f750cc6c03
MD5 2c10f7a3b8fb23b5e673e0c2f151e0cd
BLAKE2b-256 2b8eadfe71d7040b84a5568de88d631e50924e5118fb71c27be222ff27fb270a

See more details on using hashes here.

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

Hashes for fastapi_ronin-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 90fff7b7af91f6194e9d5833672cba93d03be70e2599090e7737a87ae24fafbf
MD5 73b50793d8627a17b11fd986ab622893
BLAKE2b-256 35ef9afe81dc515ea49a959e1c0d20b6141642ebc3c4a48c6a7f62b37ae94296

See more details on using hashes here.

Release history Release notifications | RSS feed

1.0.1

2 files

This release

1.0.0 This release

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