Skip to content

Custom Page

Let's create custom page parameter that is compatible with JSON API 1.0 specification.

from typing import Any, Generic, Optional, Sequence, TypeVar

from fastapi import Query
from pydantic import BaseModel
from typing_extensions import Self

from fastapi_pagination.bases import AbstractPage, AbstractParams, RawParams


class JSONAPIParams(BaseModel, AbstractParams):
    offset: int = Query(1, ge=1, alias="page[offset]")
    limit: int = Query(10, ge=1, le=100, alias="page[limit]")

    def to_raw_params(self) -> RawParams:
        return RawParams(limit=self.limit, offset=self.offset)


class JSONAPIPageInfoMeta(BaseModel):
    total: int


class JSONAPIPageMeta(BaseModel):
    page: JSONAPIPageInfoMeta


T = TypeVar("T")


class JSONAPIPage(AbstractPage[T], Generic[T]):
    data: Sequence[T]
    meta: JSONAPIPageMeta

    __params_type__ = JSONAPIParams

    @classmethod
    def create(
        cls,
        items: Sequence[T],
        params: AbstractParams,
        *,
        total: Optional[int] = None,
        **kwargs: Any,
    ) -> Self:
        assert isinstance(params, JSONAPIParams)
        assert total is not None

        return cls(
            data=items,
            meta={"page": {"total": total}},
            **kwargs,
        )

Step 1: Create pagination params

from typing import Any, Generic, Optional, Sequence, TypeVar

from fastapi import Query
from pydantic import BaseModel
from typing_extensions import Self

from fastapi_pagination.bases import AbstractPage, AbstractParams, RawParams


class JSONAPIParams(BaseModel, AbstractParams):
    offset: int = Query(1, ge=1, alias="page[offset]")
    limit: int = Query(10, ge=1, le=100, alias="page[limit]")

    def to_raw_params(self) -> RawParams:
        return RawParams(limit=self.limit, offset=self.offset)


class JSONAPIPageInfoMeta(BaseModel):
    total: int


class JSONAPIPageMeta(BaseModel):
    page: JSONAPIPageInfoMeta


T = TypeVar("T")


class JSONAPIPage(AbstractPage[T], Generic[T]):
    data: Sequence[T]
    meta: JSONAPIPageMeta

    __params_type__ = JSONAPIParams

    @classmethod
    def create(
        cls,
        items: Sequence[T],
        params: AbstractParams,
        *,
        total: Optional[int] = None,
        **kwargs: Any,
    ) -> Self:
        assert isinstance(params, JSONAPIParams)
        assert total is not None

        return cls(
            data=items,
            meta={"page": {"total": total}},
            **kwargs,
        )

Step 2: override to_raw_params method

from typing import Any, Generic, Optional, Sequence, TypeVar

from fastapi import Query
from pydantic import BaseModel
from typing_extensions import Self

from fastapi_pagination.bases import AbstractPage, AbstractParams, RawParams


class JSONAPIParams(BaseModel, AbstractParams):
    offset: int = Query(1, ge=1, alias="page[offset]")
    limit: int = Query(10, ge=1, le=100, alias="page[limit]")

    def to_raw_params(self) -> RawParams:
        return RawParams(limit=self.limit, offset=self.offset)


class JSONAPIPageInfoMeta(BaseModel):
    total: int


class JSONAPIPageMeta(BaseModel):
    page: JSONAPIPageInfoMeta


T = TypeVar("T")


class JSONAPIPage(AbstractPage[T], Generic[T]):
    data: Sequence[T]
    meta: JSONAPIPageMeta

    __params_type__ = JSONAPIParams

    @classmethod
    def create(
        cls,
        items: Sequence[T],
        params: AbstractParams,
        *,
        total: Optional[int] = None,
        **kwargs: Any,
    ) -> Self:
        assert isinstance(params, JSONAPIParams)
        assert total is not None

        return cls(
            data=items,
            meta={"page": {"total": total}},
            **kwargs,
        )

Step 3: Create custom page

from typing import Any, Generic, Optional, Sequence, TypeVar

from fastapi import Query
from pydantic import BaseModel
from typing_extensions import Self

from fastapi_pagination.bases import AbstractPage, AbstractParams, RawParams


class JSONAPIParams(BaseModel, AbstractParams):
    offset: int = Query(1, ge=1, alias="page[offset]")
    limit: int = Query(10, ge=1, le=100, alias="page[limit]")

    def to_raw_params(self) -> RawParams:
        return RawParams(limit=self.limit, offset=self.offset)


class JSONAPIPageInfoMeta(BaseModel):
    total: int


class JSONAPIPageMeta(BaseModel):
    page: JSONAPIPageInfoMeta


T = TypeVar("T")


class JSONAPIPage(AbstractPage[T], Generic[T]):
    data: Sequence[T]
    meta: JSONAPIPageMeta

    __params_type__ = JSONAPIParams

    @classmethod
    def create(
        cls,
        items: Sequence[T],
        params: AbstractParams,
        *,
        total: Optional[int] = None,
        **kwargs: Any,
    ) -> Self:
        assert isinstance(params, JSONAPIParams)
        assert total is not None

        return cls(
            data=items,
            meta={"page": {"total": total}},
            **kwargs,
        )

Step 4: override create page method

from typing import Any, Generic, Optional, Sequence, TypeVar

from fastapi import Query
from pydantic import BaseModel
from typing_extensions import Self

from fastapi_pagination.bases import AbstractPage, AbstractParams, RawParams


class JSONAPIParams(BaseModel, AbstractParams):
    offset: int = Query(1, ge=1, alias="page[offset]")
    limit: int = Query(10, ge=1, le=100, alias="page[limit]")

    def to_raw_params(self) -> RawParams:
        return RawParams(limit=self.limit, offset=self.offset)


class JSONAPIPageInfoMeta(BaseModel):
    total: int


class JSONAPIPageMeta(BaseModel):
    page: JSONAPIPageInfoMeta


T = TypeVar("T")


class JSONAPIPage(AbstractPage[T], Generic[T]):
    data: Sequence[T]
    meta: JSONAPIPageMeta

    __params_type__ = JSONAPIParams

    @classmethod
    def create(
        cls,
        items: Sequence[T],
        params: AbstractParams,
        *,
        total: Optional[int] = None,
        **kwargs: Any,
    ) -> Self:
        assert isinstance(params, JSONAPIParams)
        assert total is not None

        return cls(
            data=items,
            meta={"page": {"total": total}},
            **kwargs,
        )

Example

Now we can use JSONAPIPage in our API.

@app.get("/users")
def get_users() -> JSONAPIPage[UserOut]:
    return paginate(...)