MAX_PAGE_SIZE does not apply to GraphQL API requests #11631

Open
opened 2025-12-29 21:47:51 +01:00 by adam · 3 comments
Owner

Originally created by @jeremystretch on GitHub (Sep 18, 2025).

NetBox Edition

NetBox Community

NetBox Version

v4.4.1

Python Version

3.12

Steps to Reproduce

  1. Set MAX_PAGE_SIZE = 10 in configuration.py
  2. Restart NetBox
  3. Issue the following GraphQL API request:
{
  ip_address_list(
    pagination: {limit: 100}
  ) {
    id
    address
  }
}

Expected Behavior

A maximum of 10 IP addresses should be returned.

Observed Behavior

Up to 100 IP addresses are returned.

Originally created by @jeremystretch on GitHub (Sep 18, 2025). ### NetBox Edition NetBox Community ### NetBox Version v4.4.1 ### Python Version 3.12 ### Steps to Reproduce 1. Set `MAX_PAGE_SIZE = 10` in `configuration.py` 2. Restart NetBox 3. Issue the following GraphQL API request: ``` { ip_address_list( pagination: {limit: 100} ) { id address } } ``` ### Expected Behavior A maximum of 10 IP addresses should be returned. ### Observed Behavior Up to 100 IP addresses are returned.
adam added the type: bugnetboxstatus: backlogseverity: medium labels 2025-12-29 21:47:51 +01:00
Author
Owner

@jeremystretch commented on GitHub (Sep 18, 2025):

Dug into this a bit, but it seems that Strawberry doesn't support any mechanism for enforcing a maximum page size. I haven't been able to identify a scalable solution that doesn't involve some form of monkey-patching.

@jeremystretch commented on GitHub (Sep 18, 2025): Dug into this a bit, but it seems that Strawberry doesn't support any mechanism for enforcing a maximum page size. I haven't been able to identify a scalable solution that doesn't involve some form of monkey-patching.
Author
Owner

@arthanson commented on GitHub (Sep 29, 2025):

I think this might be possible with using PaginationWindow (https://strawberry.rocks/docs/guides/pagination/offset-based) could take limit and make it the min of MAX_PAGE_SIZE and limit.

@arthanson commented on GitHub (Sep 29, 2025): I think this might be possible with using PaginationWindow (https://strawberry.rocks/docs/guides/pagination/offset-based) could take limit and make it the min of MAX_PAGE_SIZE and limit.
Author
Owner

@arthanson commented on GitHub (Oct 8, 2025):

While we can do the code changes it would be quite a bit of changes on our end, it should probably be in Strawberry itself and would be much easier there. Given the relatively limited scope of MAX_PAGE_SIZE it probably doesn't make sense to do all the code changes. Putting back into the backlog.

Adding code here just in-case it needs to be addressed in the future. Would need a custom resolver (note: pagination_limit would need to be changed to match REST API):

from typing import Optional
from django.db import models

import strawberry
from strawberry_django.pagination import OffsetPaginationInput

from netbox.config import get_config


def enforce_pagination_limit(pagination: Optional[OffsetPaginationInput]) -> Optional[OffsetPaginationInput]:
    """
    Enforce NetBox's MAX_PAGE_SIZE configuration on pagination input.
    Returns a new OffsetPaginationInput with enforced limits if needed.
    """
    if not pagination:
        return pagination

    config = get_config()
    max_page_size = config.MAX_PAGE_SIZE

    # If no limit enforcement needed, return original
    if not max_page_size or max_page_size <= 0:
        return pagination

    # If no limit specified in pagination, return original
    if pagination.limit is None:
        return pagination

    # Enforce the limit
    if pagination.limit > max_page_size:
        print(f"GraphQL pagination limit {pagination.limit} enforced to MAX_PAGE_SIZE {max_page_size}")
        return OffsetPaginationInput(
            offset=pagination.offset,
            limit=max_page_size
        )

    return pagination


def create_paginated_resolver(model_class: models.Model):
    """
    Create a paginated resolver function that enforces MAX_PAGE_SIZE.
    Usage:
        @strawberry_django.field
        def ip_address_list(self, pagination: Optional[OffsetPaginationInput] = None) -> List[IPAddressType]:
            return create_paginated_resolver(models.IPAddress)(self, pagination)
    """
    def resolver(self, pagination: Optional[OffsetPaginationInput] = None):
        # Enforce pagination limits
        pagination = enforce_pagination_limit(pagination)

        # Get queryset
        queryset = model_class.objects.all()

        # Apply pagination
        if pagination:
            offset = pagination.offset or 0
            limit = pagination.limit
            if limit:
                queryset = queryset[offset:offset + limit]
            else:
                queryset = queryset[offset:]

        return list(queryset)

    return resolver


@strawberry.input
class NetBoxOffsetPaginationInput(OffsetPaginationInput):
    """
    Custom pagination input that enforces NetBox's MAX_PAGE_SIZE configuration.
    This is kept for potential future use but the enforce_pagination_limit function
    is the preferred approach.
    """
    pass

Then each of the schemas would need to be updated with the resolver, for example:

    @strawberry_django.field
    def ip_address_list(self, pagination: Optional[OffsetPaginationInput] = None) -> List[IPAddressType]:
        return create_paginated_resolver(models.IPAddress)(self, pagination)
@arthanson commented on GitHub (Oct 8, 2025): While we can do the code changes it would be quite a bit of changes on our end, it should probably be in Strawberry itself and would be much easier there. Given the relatively limited scope of MAX_PAGE_SIZE it probably doesn't make sense to do all the code changes. Putting back into the backlog. Adding code here just in-case it needs to be addressed in the future. Would need a custom resolver (note: pagination_limit would need to be changed to match REST API): ``` from typing import Optional from django.db import models import strawberry from strawberry_django.pagination import OffsetPaginationInput from netbox.config import get_config def enforce_pagination_limit(pagination: Optional[OffsetPaginationInput]) -> Optional[OffsetPaginationInput]: """ Enforce NetBox's MAX_PAGE_SIZE configuration on pagination input. Returns a new OffsetPaginationInput with enforced limits if needed. """ if not pagination: return pagination config = get_config() max_page_size = config.MAX_PAGE_SIZE # If no limit enforcement needed, return original if not max_page_size or max_page_size <= 0: return pagination # If no limit specified in pagination, return original if pagination.limit is None: return pagination # Enforce the limit if pagination.limit > max_page_size: print(f"GraphQL pagination limit {pagination.limit} enforced to MAX_PAGE_SIZE {max_page_size}") return OffsetPaginationInput( offset=pagination.offset, limit=max_page_size ) return pagination def create_paginated_resolver(model_class: models.Model): """ Create a paginated resolver function that enforces MAX_PAGE_SIZE. Usage: @strawberry_django.field def ip_address_list(self, pagination: Optional[OffsetPaginationInput] = None) -> List[IPAddressType]: return create_paginated_resolver(models.IPAddress)(self, pagination) """ def resolver(self, pagination: Optional[OffsetPaginationInput] = None): # Enforce pagination limits pagination = enforce_pagination_limit(pagination) # Get queryset queryset = model_class.objects.all() # Apply pagination if pagination: offset = pagination.offset or 0 limit = pagination.limit if limit: queryset = queryset[offset:offset + limit] else: queryset = queryset[offset:] return list(queryset) return resolver @strawberry.input class NetBoxOffsetPaginationInput(OffsetPaginationInput): """ Custom pagination input that enforces NetBox's MAX_PAGE_SIZE configuration. This is kept for potential future use but the enforce_pagination_limit function is the preferred approach. """ pass ``` Then each of the schemas would need to be updated with the resolver, for example: ``` @strawberry_django.field def ip_address_list(self, pagination: Optional[OffsetPaginationInput] = None) -> List[IPAddressType]: return create_paginated_resolver(models.IPAddress)(self, pagination) ```
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#11631