Skip to main content

Advanced Querying and Pagination

The BookmarkRepository provides a robust interface for querying bookmarks with support for pagination and status-based filtering. Because the repository is currently an in-memory implementation, these operations are performed directly on the internal dictionary of bookmarks, ensuring high performance for the current scale of the application.

The list_bookmarks Method

The primary entry point for advanced querying is the list_bookmarks method in app.db.repository.BookmarkRepository. It returns a slice of the data along with the total count of matching items, which is essential for building paginated user interfaces.

def list_bookmarks(
self,
page: int = 1,
per_page: int = 25,
status: Optional[str] = None,
) -> Tuple[List[Bookmark], int]:
"""Return a paginated slice of bookmarks."""
items = list(self._bookmarks.values())

# 1. Filtering
if status:
try:
target = BookmarkStatus(status)
items = [b for b in items if b.status == target]
except ValueError:
# Invalid status strings are silently ignored
pass

# 2. Sorting
items.sort(key=lambda b: b.created_at, reverse=True)

# 3. Pagination
total = len(items)
start = (page - 1) * per_page
return items[start : start + per_page], total

Filtering by Status

The repository uses the BookmarkStatus enum (defined in app.models.bookmark) to filter results. The supported status strings are:

  • active
  • archived
  • trashed

A key implementation detail is that the repository handles invalid status strings gracefully. If a string is passed that does not match a valid BookmarkStatus, the ValueError is caught, and the filter is simply not applied, returning results for all statuses instead.

Pagination Logic

Pagination in this codebase is 1-based. The page parameter starts at 1, and the offset calculation (page - 1) * per_page ensures that the correct slice of the list is returned.

  • Default Page Size: 25 items.
  • Sorting: Results are always sorted by created_at in descending order (newest first) before the pagination slice is taken. This ensures consistent results across different pages.

Integration Flow

The querying logic flows from the API layer through the service layer to the repository.

  1. Route Layer (app.routes.bookmarks): Extracts query parameters from the request.
    @bookmarks_bp.route("/", methods=["GET"])
    def list_bookmarks():
    page = request.args.get("page", 1, type=int)
    per_page = request.args.get("per_page", 25, type=int)
    status = request.args.get("status", None)
    bookmarks, total = _service.list_bookmarks(page=page, per_page=per_page, status=status)
    return jsonify({"bookmarks": [b.to_dict() for b in bookmarks], "total": total})
  2. Service Layer (app.services.bookmark_service): The BookmarkService acts as a facade, delegating the call directly to the repository.
  3. Repository Layer (app.db.repository): Performs the actual filtering, sorting, and slicing of the in-memory data.

Performance Considerations

Since the BookmarkRepository stores data in a dictionary (self._bookmarks), the list_bookmarks method must convert these values to a list and sort them on every call. While efficient for the current in-memory usage, this pattern establishes the contract that any future database-backed repository implementation must follow to support the API's pagination and filtering requirements.