While digging into some React recently, I had to integrate with an API I have running using DRF. I was using the default pagination and found that a few things were missing that would make it much easier for the front end to consume the API results.

from rest_framework import pagination
from rest_framework.response import Response
import math

class CustomPagination(pagination.PageNumberPagination):
    page_size_query_param = 'page_size'
    max_page_size = 100
    
    def get_paginated_response(self, data):
        return Response({
            'next': self.get_next_link(),
            'previous': self.get_previous_link(),
            'total': self.page.paginator.count,
            'page_size': self.get_page_size(self.request),
            'page_total': len(data),
            'max_page_size': self.max_page_size,
            'num_of_pages': math.ceil(self.page.paginator.count / self.get_page_size(self.request)),
            'results': data
        })

With the default pagination we get the following:

{
    "count": 48,
    "next": null,
    "previous": "https://serviceurl/api/customers?page=4",
    "results" : [...]
}

Now with the custom class we get a few helpers:

{
  "next": "https://serviceurl/api/customers/?page=2",
  "previous": null,
  "total": 48,
  "page_size": 10,
  "page_total": 10,
  "max_page_size": 100,
  "num_of_pages": 5,
  "results": [...]
}

You might ask why include the page_size? We have the number of pages so we can just create a promise and pull all of the pages at the same time. True, but if you want to change the page_size parameter, then the number of pages will change - calling https://serviceurl/api/customers/?page_size=100 for example:

{
  "next": null,
  "previous": null,
  "total": 48,
  "page_size": 100,
  "page_total": 48,
  "max_page_size": 100,
  "num_of_pages": 1,
  "results": [...]
}

I think it’s best to have the server side calculate this for you (as it’s already doing it). The frontend shouldn’t need to recreate the wheel here.