Skip to main content

Pagination utilities for FastAPI with SQLModel ORM

Project description

FastAPI RestKit

Python PyPI License Ruff

A complete REST toolkit for FastAPI with SQLModel ORM - featuring pagination, filtering, and sorting.

Features

  • ๐Ÿš€ Easy pagination with customizable page sizes
  • ๐Ÿ” Powerful filtering with Django-style filter lookups
  • ๐Ÿ“Š Flexible sorting with multi-field support
  • ๐Ÿ“ฆ Generic response models with full type hints
  • โšก FastAPI dependencies ready to use
  • ๐Ÿ”„ Async support out of the box
  • ๐Ÿ“ Auto-generated OpenAPI docs for all parameters

Installation

pip install fastapi-restkit

Or with uv:

uv add fastapi-restkit

Table of Contents


Quick Start

from fastapi import FastAPI, Depends
from sqlmodel import Session, select
from fastapi_restkit import PaginationParams, paginate, PaginatedResponse

app = FastAPI()

@app.get("/items", response_model=PaginatedResponse[Item])
async def list_items(
    session: Session = Depends(get_session),
    pagination: PaginationParams = Depends(),
):
    return await paginate(session, select(Item), pagination)

Pagination

Simple pagination with configurable page size:

from fastapi_restkit import PaginationParams, paginate, PaginatedResponse

@app.get("/products", response_model=PaginatedResponse[Product])
async def list_products(
    session: Session = Depends(get_session),
    pagination: PaginationParams = Depends(),
):
    query = select(Product)
    return await paginate(session, query, pagination)

URL Examples:

GET /products?page=1&page_size=20
GET /products?page=2&page_size=50

Filtering

Available Filter Types

Filter Description Value Type URL Example
SearchFilter Text search (case-insensitive) str ?name=laptop
BooleanFilter Boolean filter bool ?is_active=true
NumberFilter Numeric with comparisons float ?price=99.99
ListFilter[T] List of values (IN clause) list ?status=active&status=pending
DateFilter Date filter date ?created_at=2024-01-15
DateRangeFilter Date range (min/max) date ?created_at[min]=2024-01-01
DateTimeFilter DateTime filter datetime ?updated_at=2024-01-15T10:30:00Z
NumericRangeFilter Numeric range float ?price[min]=10&price[max]=100
TimeRangeFilter Time range time ?hours[min]=08:00&hours[max]=17:00

Lookup Operators

Filters support Django-style lookup operators:

Lookup Description SQL Example
exact Exact match (default) column = 'value'
iexact Case-insensitive equal column ILIKE 'value'
contains Contains column LIKE '%value%'
icontains Case-insensitive contains column ILIKE '%value%'
startswith Starts with column LIKE 'value%'
endswith Ends with column LIKE '%value'
gt Greater than column > value
gte Greater or equal column >= value
lt Less than column < value
lte Less or equal column <= value
isnull Is null check column IS NULL

Creating a FilterSet

from typing import Optional
from pydantic import Field
from fastapi_restkit.filterset import FilterSet, filter_as_query
from fastapi_restkit.filters import (
    SearchFilter,
    BooleanFilter,
    ListFilter,
    NumericRangeFilter,
    DateRangeFilter,
)


class ProductFilterSet(FilterSet):
    """Filters for products."""
    
    # Text search (auto-mapped to 'name' column)
    name: Optional[SearchFilter] = Field(
        default_factory=SearchFilter,
        description="Search by product name"
    )
    
    # Boolean filter
    is_active: Optional[BooleanFilter] = Field(
        default_factory=BooleanFilter,
        description="Filter by active status"
    )
    
    # List filter (IN clause)
    category: Optional[ListFilter[str]] = Field(
        default_factory=ListFilter,
        description="Filter by categories"
    )
    
    # Numeric range
    price: Optional[NumericRangeFilter] = Field(
        default_factory=NumericRangeFilter,
        description="Filter by price range"
    )
    
    # Date range
    created_at: Optional[DateRangeFilter] = Field(
        default_factory=DateRangeFilter,
        description="Filter by creation date"
    )
    
    # Multi-column search
    search: Optional[SearchFilter] = Field(
        default_factory=SearchFilter,
        description="Search in name and description"
    )

    class Config:
        # Custom column mapping
        field_columns = {
            "search": ["name", "description"],  # OR search across columns
        }


# Use in endpoint
@app.get("/products")
async def list_products(
    session: Session = Depends(get_session),
    filters: ProductFilterSet = Depends(filter_as_query(ProductFilterSet)),
):
    query = select(Product)
    query = filters.apply_to_query(query, Product)
    return session.exec(query).all()

Filter URL Examples

# Simple text search
GET /products?name=laptop

# With lookup operator
GET /products?name=lap&name[lookup]=startswith

# Boolean filter
GET /products?is_active=true

# List filter (IN clause)
GET /products?category=electronics&category=computers

# Numeric range
GET /products?price[min]=100&price[max]=500

# Date range
GET /products?created_at[min]=2024-01-01&created_at[max]=2024-12-31

# Combined filters
GET /products?is_active=true&category=electronics&price[min]=100

Sorting

Creating a SortingSet

from fastapi_restkit.sortingset import SortingSet, SortableField, sorting_as_query


class ProductSortingSet(SortingSet):
    """Sorting options for products."""
    
    id: SortableField = SortableField(description="Product ID")
    name: SortableField = SortableField(description="Product name")
    price: SortableField = SortableField(description="Price")
    created_at: SortableField = SortableField(description="Creation date")
    
    # Map to different column name
    updated: SortableField = SortableField(
        description="Update date",
        column="updated_at",
    )

    class Config:
        default_sorting = ["created_at:desc"]


# Use in endpoint
@app.get("/products")
async def list_products(
    session: Session = Depends(get_session),
    sorting: ProductSortingSet = Depends(sorting_as_query(ProductSortingSet)),
):
    query = select(Product)
    query = sorting.apply_to_query(query, Product)
    return session.exec(query).all()

Sorting URL Examples

# Ascending (default)
GET /products?sort_by=name

# Descending
GET /products?sort_by=price:desc

# Multiple fields
GET /products?sort_by=category:asc&sort_by=price:desc

# Results in: ORDER BY category ASC, price DESC

Complete Example

Combining pagination, filtering, and sorting:

from fastapi import APIRouter, Depends
from sqlmodel import Session, select
from fastapi_restkit import PaginationParams, paginate, PaginatedResponse
from fastapi_restkit.filterset import FilterSet, filter_as_query
from fastapi_restkit.sortingset import SortingSet, SortableField, sorting_as_query
from fastapi_restkit.filters import SearchFilter, BooleanFilter, NumericRangeFilter


class ProductFilterSet(FilterSet):
    name: Optional[SearchFilter] = Field(default_factory=SearchFilter)
    is_active: Optional[BooleanFilter] = Field(default_factory=BooleanFilter)
    price: Optional[NumericRangeFilter] = Field(default_factory=NumericRangeFilter)


class ProductSortingSet(SortingSet):
    name: SortableField = SortableField(description="Name")
    price: SortableField = SortableField(description="Price")
    created_at: SortableField = SortableField(description="Created")

    class Config:
        default_sorting = ["created_at:desc"]


router = APIRouter(prefix="/products", tags=["Products"])


@router.get("", response_model=PaginatedResponse[ProductRead])
async def list_products(
    session: Session = Depends(get_session),
    filters: ProductFilterSet = Depends(filter_as_query(ProductFilterSet)),
    sorting: ProductSortingSet = Depends(sorting_as_query(ProductSortingSet)),
    pagination: PaginationParams = Depends(),
):
    """
    List products with filtering, sorting, and pagination.
    
    **Filters:** name, is_active, price
    **Sorting:** name, price, created_at
    **Pagination:** page, page_size
    """
    query = select(Product)
    query = filters.apply_to_query(query, Product)
    query = sorting.apply_to_query(query, Product)
    return await paginate(session, query, pagination)

Combined URL Example:

GET /products?is_active=true&price[min]=100&price[max]=500&sort_by=price:asc&page=1&page_size=20

Development

Setup

# Clone the repository
git clone https://github.com/cacenot/fastapi-restkit.git
cd fastapi-restkit

# Install dependencies with uv
uv sync --dev

# Run tests
uv run pytest

# Run linter
uv run ruff check .

# Run formatter
uv run ruff format .

Project Structure

fastapi-restkit/
โ”œโ”€โ”€ src/
โ”‚   โ””โ”€โ”€ fastapi_restkit/
โ”‚       โ”œโ”€โ”€ __init__.py
โ”‚       โ”œโ”€โ”€ pagination.py     # Pagination logic
โ”‚       โ”œโ”€โ”€ filters.py        # Filter types
โ”‚       โ”œโ”€โ”€ filterset.py      # FilterSet base class
โ”‚       โ”œโ”€โ”€ sorting.py        # Sorting utilities
โ”‚       โ”œโ”€โ”€ sortingset.py     # SortingSet base class
โ”‚       โ”œโ”€โ”€ models.py         # Response models
โ”‚       โ””โ”€โ”€ dependencies.py   # FastAPI dependencies
โ”œโ”€โ”€ tests/
โ”œโ”€โ”€ docs/
โ””โ”€โ”€ pyproject.toml

License

MIT License - see LICENSE for details.

Project details


Download files

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

Source Distribution

fastapi_restkit-0.1.2.tar.gz (21.9 kB view details)

Uploaded Source

Built Distribution

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

fastapi_restkit-0.1.2-py3-none-any.whl (26.8 kB view details)

Uploaded Python 3

File details

Details for the file fastapi_restkit-0.1.2.tar.gz.

File metadata

  • Download URL: fastapi_restkit-0.1.2.tar.gz
  • Upload date:
  • Size: 21.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.19

File hashes

Hashes for fastapi_restkit-0.1.2.tar.gz
Algorithm Hash digest
SHA256 c0aad22ae6f7392699cb840a8e5cd33ea878e039a50a02a6de91aef65aee4c60
MD5 5a51e90b2eb5d947a3fd53d05875009f
BLAKE2b-256 7babc89c8d7d5061ce7522f00b1c4bb3c17e56b43d15f2209510fd21bb3f73ab

See more details on using hashes here.

File details

Details for the file fastapi_restkit-0.1.2-py3-none-any.whl.

File metadata

File hashes

Hashes for fastapi_restkit-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 bc0868fbfa7e746ec555e484a0c76c0fc92b9220baa6f991eb0a93a1655c5f84
MD5 597179654c4dcecc0390f3ed2d655c22
BLAKE2b-256 6d3bbb30f8d49f3b750480f1dd2ca5bcaaa9935b51b9d67e6d2ca8bb56107368

See more details on using hashes here.

Supported by

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