Pagination utilities for FastAPI with SQLModel ORM
Project description
FastAPI RestKit
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
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_restkit-0.1.1.tar.gz.
File metadata
- Download URL: fastapi_restkit-0.1.1.tar.gz
- Upload date:
- Size: 21.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
23033ad5481b566921c177b29f91415540edcdd400d3f8cdfd05c1e4717bcb74
|
|
| MD5 |
7812727d31cab1077a1a018949c38771
|
|
| BLAKE2b-256 |
d6616e9bb9fe0054880b12ec7b57a65f7d70f9eb2b00dea4d3b29baa963c0897
|
File details
Details for the file fastapi_restkit-0.1.1-py3-none-any.whl.
File metadata
- Download URL: fastapi_restkit-0.1.1-py3-none-any.whl
- Upload date:
- Size: 26.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fb2ae324952ad5153eb2f17002ccc7cd8c49550dfd0504326776b9fe17cb78a2
|
|
| MD5 |
c3985f726fb21a951af8e1ec0cb0e2e1
|
|
| BLAKE2b-256 |
c55a93c6dce30446de044695a3d587bc8ae8359b374d3b61a6bd1795dac967e5
|