Metadata field

Useful data about pagination.

In the PaginationService, let's first create a method to calculate the offset from the limit and page. In the first page, no items are skipped. And for each page after the first one, we should skip the same amount of items as in limit. With this, offset will always be consistent.

calculateOffset(limit: number, page: number) {
  return (page - 1) * limit;
}

Then, before creating the method which will create the meta field, let's first create an interface to represent this metadata in querying/interfaces/pagination-meta.interface.

export interface PaginationMeta {
  readonly itemsPerPage: number;
  readonly totalItems: number;
  readonly currentPage: number;
  readonly totalPages: number;
  readonly hasNextPage: boolean;
  readonly hasPreviousPage: boolean;
}

And finally, the method that creates the meta field. Its steps are:

  • Calculate the page count, dividing the item count by the page size, and rounding it up in case of a decimal result (last page with fewer items)

  • Check if the current page is greater than the last page, in such case no metadata will be returned

  • Define if there are next/previous pages from the current page

  • Return all the metadata

createMeta(limit: number, page: number, count: number): PaginationMeta {
  const totalPages = Math.ceil(count / limit);
  if (page > totalPages) return;

  const hasNextPage = page < totalPages;
  const hasPreviousPage = page > 1;

  return {
    itemsPerPage: limit,
    totalItems: count,
    currentPage: page,
    totalPages,
    hasNextPage,
    hasPreviousPage,
  };
}

What remains to be done now is to inject the PaginationService in the ProductsService, and adjust the logic of the findAll() method.

async findAll(paginationDto: PaginationDto) {
  const { page } = paginationDto;
  const limit = paginationDto.limit ?? DefaultPageSize.PRODUCTS;
  const offset = this.paginationService.calculateOffset(limit, page);

  const [data, count] = await this.productsRepository.findAndCount({
    skip: offset,
    take: limit,
  });
  const meta = this.paginationService.createMeta(limit, page, count);

  return { data, meta };
}

We can then do the same with the remaining findAll() methods to finish this part of pagination.

Commit - Incrementing pagination with metadata field

There is also another kind of pagination: the cursor-based pagination. It has some interesting advantages over the offset-based pagination but also some drawbacks.

I recommend this article by Matej Bačo (blog) in case there is interest to learn more about it.

Last updated