Skip to content

Question: How to use Orval-generated hooks with header-based pagination? #2112

@hubertokf

Description

@hubertokf

First of all

Thanks for your work on Orval! Any guidance or clarification on how to better handle these situations would be greatly appreciated.

Summary:

I'm using Orval to generate React Query hooks in my React application. My API returns pagination metadata in HTTP headers:

x-pagination-limit
x-pagination-offset
x-pagination-total-count

The response body is just an array of items.

Question 1: Accessing Headers

useInfiniteQuery requires getNextPageParam, but this function only receives the response body (lastPage), without access to headers.

Orval-generated hooks assume the fetcher returns only the parsed response body.

What I've tried

  • I tried using a global mutator to return { data, headers }, but that changes the return type for all endpoints, breaking type safety and requiring major changes across the codebase.
  • I tried using a global mutator to return AxiosResponse, that worked, i got access to axios response but the data type doesn't reflect it. I had to every time override the type.

There doesn't seem to be a clean way to access headers alongside the response data when using the generated hooks.

Is there a recommended way to handle this?

  • Is it possible to configure Orval to expose both response data and headers in generated hooks?
  • Can Orval support per-endpoint mutators or response transformations, so that only specific endpoints need to return { data, headers }?
  • Or is there an existing pattern in Orval for working with header-based pagination, particularly with useInfiniteQuery?

Question 1: Pass page params

How to pass the pageParams generated by the getNextPageParam function to the payload? It doesn't inject by itself.?

Example of the scenario:

Response headers:

x-pagination-limit: 10
x-pagination-offset: 20
x-pagination-total-count: 100

Response body:

[
  { "id": 1, "name": "Item 1" },
  { "id": 2, "name": "Item 2" }
]

Examples

Mutator used to return AxiosResponse

export function customInstance<T>(config: AxiosRequestConfig, options?: AxiosRequestConfig & {returnResponse?: boolean}): Promise<T>
export function customInstance<T>(config: AxiosRequestConfig, options?: AxiosRequestConfig & {returnResponse?: boolean}): Promise<AxiosResponse<T>> {
  const accessToken = accessTokenManager.getToken();

  const headers = new AxiosHeaders();

  if (accessToken) {
    headers.set('Authorization', `Bearer ${accessToken}`);
  }

  const source = Axios.CancelToken.source();
  const promise = AXIOS_INSTANCE({
    ...config,
    ...options,
    cancelToken: source.token,
    headers: {
      ...config.headers,
      ...headers,
      ...options?.headers,
    },
  }).then((response) =>
    // this is a workaround to return the response object instead of the data.
    options?.returnResponse ? response : response.data);

  // @ts-expect-error cancel is not defined
  promise.cancel = () => {
    source.cancel('Query was cancelled');
  };

  return promise;
};

Use of seInfinity

const {
    data: response, // type: ProductPackage[] but should be something like UseInfiniteQueryResult<ProductPackage[]>
    isLoading,
    isError,
    error,
    refetch,
    fetchNextPage,
  } = useGetAppProductPackagesInfinite(
    selectedAccount?.appId || '',
    {...payload},
    {
      request: {
        returnResponse: true,
      },
      query: {
        queryKey: getGetAppProductPackagesQueryKey(),
        initialPageParam: {
          page: 1,
          limit: 20,
          offset: 0,
        },
        getNextPageParam: (lastPage) => {
          console.log('lastPage', lastPage); // type: ProductPackage[] but should be AxiosResponse<ProductPackage[]>

          // just a test
          return {
            page: 1,
            limit: 20,
            offset: 20,
          };
        },
      },
    },
  );

Environment

Orval version: 7.5.0
React Query: 5.49.2
React: 18.3.1
API design: Pagination metadata in HTTP headers

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionFurther information is requested

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions