Filter, Sort, and Paginate (FSP) utilities for FastAPI + SQLModel
Project description
fastapi-fsp
Filter, Sort, and Paginate (FSP) utilities for FastAPI + SQLModel.
fastapi-fsp helps you build standardized list endpoints that support:
- Filtering on arbitrary fields with rich operators (eq, ne, lt, lte, gt, gte, in, between, like/ilike, null checks, contains/starts_with/ends_with)
- Sorting by field (asc/desc)
- Pagination with page/per_page and convenient HATEOAS links
It is framework-friendly: you declare it as a FastAPI dependency and feed it a SQLModel/SQLAlchemy Select query and a Session.
Installation
Using uv (recommended):
# create & activate virtual env with uv
uv venv
. .venv/bin/activate
# add runtime dependency
uv add fastapi-fsp
Using pip:
pip install fastapi-fsp
Quick start
Below is a minimal example using FastAPI and SQLModel.
from typing import Optional
from fastapi import Depends, FastAPI
from sqlmodel import Field, SQLModel, Session, create_engine, select
from fastapi_fsp.fsp import FSPManager
from fastapi_fsp.models import PaginatedResponse
class HeroBase(SQLModel):
name: str = Field(index=True)
secret_name: str
age: Optional[int] = Field(default=None, index=True)
class Hero(HeroBase, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
class HeroPublic(HeroBase):
id: int
engine = create_engine("sqlite:///database.db", connect_args={"check_same_thread": False})
SQLModel.metadata.create_all(engine)
app = FastAPI()
def get_session():
with Session(engine) as session:
yield session
@app.get("/heroes/", response_model=PaginatedResponse[HeroPublic])
def read_heroes(*, session: Session = Depends(get_session), fsp: FSPManager = Depends(FSPManager)):
query = select(Hero)
return fsp.generate_response(query, session)
Run the app and query:
- Pagination:
GET /heroes/?page=1&per_page=10 - Sorting:
GET /heroes/?sort_by=name&order=asc - Filtering:
GET /heroes/?field=age&operator=gte&value=21
The response includes data, meta (pagination, filters, sorting), and links (self, first, next, prev, last).
Query parameters
Pagination:
- page: integer (>=1), default 1
- per_page: integer (1..100), default 10
Sorting:
- sort_by: the field name, e.g.,
name - order:
ascordesc
Filtering (repeatable sets; arrays are supported by sending multiple parameters):
- field: the field/column name, e.g.,
name - operator: one of
- eq, ne
- lt, lte, gt, gte
- in, not_in (comma-separated values)
- between (two comma-separated values)
- like, not_like
- ilike, not_ilike (if backend supports ILIKE)
- is_null, is_not_null
- contains, starts_with, ends_with (translated to LIKE patterns)
- value: raw string value (or list-like comma-separated depending on operator)
Examples:
?field=name&operator=eq&value=Deadpond?field=age&operator=between&value=18,30?field=name&operator=in&value=Deadpond,Rusty-Man?field=name&operator=contains&value=man
You can chain multiple filters by repeating the triplet:
?field=age&operator=gte&value=18&field=name&operator=ilike&value=rust
Response model
{
"data": [ ... ],
"meta": {
"pagination": {
"total_items": 42,
"per_page": 10,
"current_page": 1,
"total_pages": 5
},
"filters": [
{"field": "name", "operator": "eq", "value": "Deadpond"}
],
"sort": {"sort_by": "name", "order": "asc"}
},
"links": {
"self": "/heroes/?page=1&per_page=10",
"first": "/heroes/?page=1&per_page=10",
"next": "/heroes/?page=2&per_page=10",
"prev": null,
"last": "/heroes/?page=5&per_page=10"
}
}
Development
This project uses uv as the package manager.
- Create env and sync deps:
uv venv
. .venv/bin/activate
uv sync --dev
- Run lint and format checks:
uv run ruff check .
uv run ruff format --check .
- Run tests:
uv run pytest -q
- Build the package:
uv build
CI/CD and Releases
GitHub Actions workflows are included:
- CI (lint + tests) runs on pushes and PRs.
- Release: pushing a tag matching
v*.*.*runs tests, builds, and publishes to PyPI usingPYPI_API_TOKENsecret.
To release:
- Update the version in
pyproject.toml. - Push a tag, e.g.
git tag v0.1.1 && git push origin v0.1.1. - Ensure the repository has
PYPI_API_TOKENsecret set (an API token from PyPI).
License
MIT License. See LICENSE.
Project details
Release history Release notifications | RSS feed
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_fsp-0.1.0.tar.gz.
File metadata
- Download URL: fastapi_fsp-0.1.0.tar.gz
- Upload date:
- Size: 32.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e97e9e5ee66ae9d219df8e68cceca6b2be51ddf1a7210fd21068b3f81d71f81b
|
|
| MD5 |
850ec123617af183041cb20a3260d0e4
|
|
| BLAKE2b-256 |
3127ab1153215f8a47e4a46db46c63e6941d2dcbc61fb3df09e20672211d85ea
|
File details
Details for the file fastapi_fsp-0.1.0-py3-none-any.whl.
File metadata
- Download URL: fastapi_fsp-0.1.0-py3-none-any.whl
- Upload date:
- Size: 7.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
60c58d9db8f593096dd20ec2cbb0d413ba931eddd0c1689a8b406626a845fa1e
|
|
| MD5 |
d54031fb9f0990a4a377614571fe4b76
|
|
| BLAKE2b-256 |
eeb76329199c446cbb51ef2aa0eb01e72df223e7b5fc5467d8d1962540a47c9e
|