![Tests](https://github.com/JGoutin/fastapi_paginator/workflows/tests/badge.svg)
[![codecov](https://codecov.io/gh/JGoutin/fastapi_paginator/branch/main/graph/badge.svg?token=QR5nYeX11F)](https://codecov.io/gh/JGoutin/fastapi_paginator)
[![PyPI](https://img.shields.io/pypi/v/fastapi_paginator.svg)](https://pypi.org/project/fastapi_paginator)
# FastAPI Paginator
Easy to use paginator for [FastAPI](https://fastapi.tiangolo.com/)
Currently, supports only [encode/databases](https://github.com/encode/databases) as
database library and tested with SQlite and PostgreSQL.
## Features
* Simple FastAPI integration.
* Navigation with page numbers (With total page count returned on first page).
* Navigation from a specific row (since).
* Ordering result (On multiple columns).
* Filtering result using various SQL functions.
## Installation
FastAPI Paginator is available on PyPI, so it can be installed like any other Python
package.
Example with Pip:
```bash
pip install fastapi_paginator
```
## Usage
### Routes creations in FastAPI
To use it, you only need to create a `fastapi_paginator.Paginator` instance linked to
the database and routes
using `fastapi_paginator.PageParameters` and `fastapi_paginator.Page`.
```python
import databases
import fastapi
import pydantic
import sqlalchemy
import fastapi_paginator
# Already existing database, FastAPI application, "item" table, and "item" model
database = databases.Database(f"sqlite:///local.db}")
app = fastapi.FastAPI()
table = sqlalchemy.Table(
"table",
sqlalchemy.MetaData(),
Column("id", sqlalchemy.Integer, primary_key=True),
Column("name", sqlalchemy.String, nullable=False),
)
class Item(pydantic.BaseModel):
"""Item in database."""
class Config:
"""Config."""
orm_mode = True # Required
id: int
name: str
# Create a paginator for the database (Required only once per database)
paginator = fastapi_paginator.Paginator(database)
# Create a paginated route
@app.get("/list")
async def list_data(
page_parameters: fastapi_paginator.PageParameters = Depends(),
) -> fastapi_paginator.Page[Item]:
"""List data with pagination."""
return await paginator(table.select(), Item, page_parameters)
```
### Paginated routes usage from clients
#### Request
Paginator parameters are passed as query parameters, for instance:
```http request
GET /list?order_by=id&page=2
```
##### Query parameters
###### page
The page to return.
When page is not specified or equal to `1`, the request returns `total_page` that is
the maximum number of pages.
*Cannot be used with `since`.*
###### since
The item from where starting to return the result.
When navigating between successive pages, the `next_since` returned value should be used
as `since` for the subsequent requests.
*Cannot be used with `page`*.
*Cannot be used with `order_by` if not ordering on the field used by `since`*.
###### order_by
Sort the resulting items by the specified field name.
Order is descending if `-` is added before the field name, else order is ascending.
This query parameter can be specified multiple time to sort by multiple columns.
**Example:**
"Ordering descending by the `created_at` column: `order_by=-created_at`
###### filter_by
Filter the resulting items.
The query must be in the form `field_name operator argument`, with:
* `field_name`: the name on the field on where apply the filter.
* `operator`: one operator from the list bellow.
* `argument`: is the operator argument, it can be one or more value separated by `,`
(Depending on the operator), valid values must be a primitive JSON type like
numbers, double-quoted strings, `true`, `false` and `null`.
This query parameter can be specified multiple time to filter on more criteria
(Using AND logical conjunction).
Available operators:
* `=`: Equal to a single value (Also supports `null`, `true` and `false`)
* `<`: Lower than a single value.
* `<=`: Lower or equal than a single value.
* `>`: Greater than a single value.
* `>=`: Greater or equal than a single value.
* `between`: Between a pair of values (`value_1` <= `field_value` <= `value_2`).
* `in`: Present in a list of one or more values.
* `like`: Like a single value (`%` can be used as wildcard for zero to multiple
characters, `_` as wildcard for a single character, `/` can be used as escape
character for `%` and `_`).
* `ilike`: Same as `like`, but case insensitive.
* `startswith`: String representation starts with a single value.
* `endswith`: String representation ends with a single value.
* `contains`: String representation contains a single value.
Any operator can be negated by adding `!` in front of it.
*Warning*: Depending on your HTTP client, the query parameter value may require to be
URL encoded.
**Example:**
Returning only data with a `name` field that does not start with
`Product`: `filter_by=name%20%21like%20%22Product%25%22`
(With URL encoded value of: `name !like "Product%"`')
##### Response
The response is a JSON dictionnary with the following fields:
* `items`: The list returned items.
* `next_since`: Next value to use with `since` query parameter.
* `next_page`: Next value to use with `page` query parameter.
* `total_pages`: Total pages, only computed and returned when on page 1
### Using alternates JSON libraries
It is possible to override the `json.loads` function used in all paginator as follows
(Example with [orjson](https://github.com/ijl/orjson)):
```python
import orjson
import fastapi_paginator
fastapi_paginator.Paginator.json_loads = orjson.loads
```
Raw data
{
"_id": null,
"home_page": "https://github.com/JGoutin/fastapi_paginator",
"name": "fastapi-paginator",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.10,<4.0",
"maintainer_email": "",
"keywords": "paginator,fastapi,sqlalchemy",
"author": "JGoutin",
"author_email": "",
"download_url": "https://files.pythonhosted.org/packages/b1/f9/abb23811075331d36f98b5f8363cb029727e5a0fff1cf4c9a64201843fa5/fastapi_paginator-0.1.4.tar.gz",
"platform": null,
"description": "![Tests](https://github.com/JGoutin/fastapi_paginator/workflows/tests/badge.svg)\n[![codecov](https://codecov.io/gh/JGoutin/fastapi_paginator/branch/main/graph/badge.svg?token=QR5nYeX11F)](https://codecov.io/gh/JGoutin/fastapi_paginator)\n[![PyPI](https://img.shields.io/pypi/v/fastapi_paginator.svg)](https://pypi.org/project/fastapi_paginator)\n\n# FastAPI Paginator\n\nEasy to use paginator for [FastAPI](https://fastapi.tiangolo.com/)\n\nCurrently, supports only [encode/databases](https://github.com/encode/databases) as \ndatabase library and tested with SQlite and PostgreSQL.\n\n## Features\n\n* Simple FastAPI integration.\n* Navigation with page numbers (With total page count returned on first page).\n* Navigation from a specific row (since).\n* Ordering result (On multiple columns).\n* Filtering result using various SQL functions.\n\n## Installation\n\nFastAPI Paginator is available on PyPI, so it can be installed like any other Python \npackage.\n\nExample with Pip:\n```bash\npip install fastapi_paginator\n```\n\n## Usage\n\n### Routes creations in FastAPI\n\nTo use it, you only need to create a `fastapi_paginator.Paginator` instance linked to\nthe database and routes\nusing `fastapi_paginator.PageParameters` and `fastapi_paginator.Page`.\n\n```python\nimport databases\nimport fastapi\nimport pydantic\nimport sqlalchemy\nimport fastapi_paginator\n\n# Already existing database, FastAPI application, \"item\" table, and \"item\" model\ndatabase = databases.Database(f\"sqlite:///local.db}\")\napp = fastapi.FastAPI()\n\ntable = sqlalchemy.Table(\n \"table\",\n sqlalchemy.MetaData(),\n Column(\"id\", sqlalchemy.Integer, primary_key=True),\n Column(\"name\", sqlalchemy.String, nullable=False),\n)\n\nclass Item(pydantic.BaseModel):\n \"\"\"Item in database.\"\"\"\n\n class Config:\n \"\"\"Config.\"\"\"\n\n orm_mode = True # Required\n\n id: int\n name: str\n\n\n# Create a paginator for the database (Required only once per database)\npaginator = fastapi_paginator.Paginator(database)\n\n# Create a paginated route\n@app.get(\"/list\")\nasync def list_data(\n page_parameters: fastapi_paginator.PageParameters = Depends(),\n) -> fastapi_paginator.Page[Item]:\n \"\"\"List data with pagination.\"\"\"\n return await paginator(table.select(), Item, page_parameters)\n```\n\n### Paginated routes usage from clients\n\n\n#### Request\nPaginator parameters are passed as query parameters, for instance:\n\n```http request\nGET /list?order_by=id&page=2\n```\n\n##### Query parameters\n\n###### page\nThe page to return.\n\nWhen page is not specified or equal to `1`, the request returns `total_page` that is\nthe maximum number of pages.\n\n*Cannot be used with `since`.*\n\n###### since\n\nThe item from where starting to return the result.\n\nWhen navigating between successive pages, the `next_since` returned value should be used\nas `since` for the subsequent requests.\n\n*Cannot be used with `page`*.\n\n*Cannot be used with `order_by` if not ordering on the field used by `since`*.\n\n###### order_by\nSort the resulting items by the specified field name.\n\nOrder is descending if `-` is added before the field name, else order is ascending.\n\nThis query parameter can be specified multiple time to sort by multiple columns.\n\n**Example:**\n\"Ordering descending by the `created_at` column: `order_by=-created_at`\n\n###### filter_by\n\nFilter the resulting items.\n\nThe query must be in the form `field_name operator argument`, with:\n * `field_name`: the name on the field on where apply the filter.\n * `operator`: one operator from the list bellow.\n * `argument`: is the operator argument, it can be one or more value separated by `,`\n (Depending on the operator), valid values must be a primitive JSON type like \n numbers, double-quoted strings, `true`, `false` and `null`.\n\nThis query parameter can be specified multiple time to filter on more criteria\n(Using AND logical conjunction).\n\nAvailable operators:\n * `=`: Equal to a single value (Also supports `null`, `true` and `false`)\n * `<`: Lower than a single value.\n * `<=`: Lower or equal than a single value.\n * `>`: Greater than a single value.\n * `>=`: Greater or equal than a single value.\n * `between`: Between a pair of values (`value_1` <= `field_value` <= `value_2`).\n * `in`: Present in a list of one or more values.\n * `like`: Like a single value (`%` can be used as wildcard for zero to multiple\n characters, `_` as wildcard for a single character, `/` can be used as escape\n character for `%` and `_`).\n * `ilike`: Same as `like`, but case insensitive.\n * `startswith`: String representation starts with a single value.\n * `endswith`: String representation ends with a single value.\n * `contains`: String representation contains a single value.\n\nAny operator can be negated by adding `!` in front of it.\n\n*Warning*: Depending on your HTTP client, the query parameter value may require to be\nURL encoded.\n\n**Example:**\nReturning only data with a `name` field that does not start with\n`Product`: `filter_by=name%20%21like%20%22Product%25%22`\n(With URL encoded value of: `name !like \"Product%\"`')\n\n##### Response\n\nThe response is a JSON dictionnary with the following fields:\n* `items`: The list returned items.\n* `next_since`: Next value to use with `since` query parameter.\n* `next_page`: Next value to use with `page` query parameter.\n* `total_pages`: Total pages, only computed and returned when on page 1\n\n### Using alternates JSON libraries\n\nIt is possible to override the `json.loads` function used in all paginator as follows \n(Example with [orjson](https://github.com/ijl/orjson)):\n\n```python\nimport orjson\nimport fastapi_paginator\n\n\nfastapi_paginator.Paginator.json_loads = orjson.loads\n```\n",
"bugtrack_url": null,
"license": "BSD-2-Clause",
"summary": "Paginator for FastAPI",
"version": "0.1.4",
"split_keywords": [
"paginator",
"fastapi",
"sqlalchemy"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "c05832f234c6f26401c30c9e9beafb36ce836daae14eb91ffe08afa47ee06beb",
"md5": "5d30ca5f76a1d09d237ce71b1f85f44f",
"sha256": "aeb49cb09451cfe194c2457e7deaaf47b2edb42afe74ada41ce2ec4f5fd5ee5b"
},
"downloads": -1,
"filename": "fastapi_paginator-0.1.4-py3-none-any.whl",
"has_sig": false,
"md5_digest": "5d30ca5f76a1d09d237ce71b1f85f44f",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10,<4.0",
"size": 10085,
"upload_time": "2023-02-09T10:34:49",
"upload_time_iso_8601": "2023-02-09T10:34:49.831556Z",
"url": "https://files.pythonhosted.org/packages/c0/58/32f234c6f26401c30c9e9beafb36ce836daae14eb91ffe08afa47ee06beb/fastapi_paginator-0.1.4-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "b1f9abb23811075331d36f98b5f8363cb029727e5a0fff1cf4c9a64201843fa5",
"md5": "a7e387afcf4400d0d0f2efe7a1e0c682",
"sha256": "68918dc16d8f6e89e5406cf974d54313ff62c29642f132f654b49b104fa20f48"
},
"downloads": -1,
"filename": "fastapi_paginator-0.1.4.tar.gz",
"has_sig": false,
"md5_digest": "a7e387afcf4400d0d0f2efe7a1e0c682",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10,<4.0",
"size": 10340,
"upload_time": "2023-02-09T10:34:51",
"upload_time_iso_8601": "2023-02-09T10:34:51.574039Z",
"url": "https://files.pythonhosted.org/packages/b1/f9/abb23811075331d36f98b5f8363cb029727e5a0fff1cf4c9a64201843fa5/fastapi_paginator-0.1.4.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-02-09 10:34:51",
"github": true,
"gitlab": false,
"bitbucket": false,
"github_user": "JGoutin",
"github_project": "fastapi_paginator",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "fastapi-paginator"
}