Skip to content

# Pagination

### Use Case 4: Pagination & Filtering

Scenario: Blog post API with pagination and filtering.

Python

    from mongo_ops import BaseDocument, BaseRepository
    from pydantic import BaseModel, Field
    from typing import List, Optional, Generic, TypeVar

    T = TypeVar("T")

    class PaginatedResponse(BaseModel, Generic[T]):
        items: List[T]
        total: int
        page: int
        page_size: int
        total_pages: int
        has_next: bool
        has_prev: bool

    class BlogPost(BaseDocument):
        title: str
        content: str
        author_id: str
        published: bool = False
        tags: List[str] = []
        views: int = 0

    class BlogPostRepository(BaseRepository[BlogPost]):
        def __init__(self):
            super().__init__("blog_posts", BlogPost)

        async def paginate(
            self,
            page: int = 1,
            page_size: int = 10,
            filter_dict: Optional[dict] = None,
            sort_by: str = "created_at",
            sort_order: int = -1
        ) -> PaginatedResponse[BlogPost]:
            """Get paginated blog posts"""
            filter_dict = filter_dict or {}
            skip = (page - 1) * page_size

            # Get total count
            total = await self.count(filter_dict)

            # Get items
            items = await self.get_many(
                filter=filter_dict,
                skip=skip,
                limit=page_size,
                sort=[(sort_by, sort_order)]
            )

            total_pages = (total + page_size - 1) // page_size

            return PaginatedResponse(
                items=items,
                total=total,
                page=page,
                page_size=page_size,
                total_pages=total_pages,
                has_next=page < total_pages,
                has_prev=page > 1
            )

        async def get_by_author(self, author_id: str, published_only: bool = True):
            """Get posts by author"""
            filter_dict = {"author_id": author_id}
            if published_only:
                filter_dict["published"] = True
            return await self.get_many(filter=filter_dict, sort=[("created_at", -1)])

        async def search_by_tags(self, tags: List[str]) -> List[BlogPost]:
            """Search posts by tags"""
            return await self.get_many(filter={"tags": {"$in": tags}, "published": True})

    # Usage in FastAPI
    @app.get("/posts/", response_model=PaginatedResponse[BlogPost])
    async def list_posts(
        page: int = 1,
        page_size: int = 10,
        published: Optional[bool] = None,
        author_id: Optional[str] = None
    ):
        filter_dict = {}
        if published is not None:
            filter_dict["published"] = published
        if author_id:
            filter_dict["author_id"] = author_id

        return await blog_repo.paginate(page, page_size, filter_dict)

    @app.get("/posts/author/{author_id}", response_model=List[BlogPost])
    async def posts_by_author(author_id: str, published: bool = True):
        return await blog_repo.get_by_author(author_id, published)

    @app.get("/posts/tags", response_model=List[BlogPost])
    async def posts_by_tags(tags: List[str] = Query(...)):
        return await blog_repo.search_by_tags(tags)