Skip to content

Conversation

@tk1024
Copy link
Contributor

@tk1024 tk1024 commented Nov 24, 2025

This is the same as #2586 but for the master branch.

Problem

The SWR code generator has several type safety and parameter handling issues:

  1. Missing Pagination Support: useSWRInfinite implementation doesn't support pagination parameters:

    • swrFn doesn't accept parameters from the key loader
    • Pagination parameters cannot be passed to the API function
    • Makes infinite queries unusable for paginated APIs
  2. Function Wrapping Bug: swrKeyLoader is incorrectly wrapped in an arrow function:

    • The wrapper ignores SWR's (pageIndex, previousPageData) arguments
    • Prevents proper pagination logic in infinite queries
    • Required workarounds to fix generated code
  3. Missing Headers in useSWRInfinite: When headers: true is enabled, headers are not passed to the swrFn:

    • Headers defined in OpenAPI spec are ignored in infinite queries
    • Causes TypeScript compilation errors when headers are required
    • Runtime errors when API requires headers
  4. Missing Headers in Mutation Fetchers: Mutation hooks and fetchers don't include header parameters:

    • useXXXMutation hooks don't accept headers
    • Mutation fetchers don't pass headers to API functions
    • Breaking for APIs that require headers in POST/PUT/DELETE operations

Solution

This PR fixes all issues:

  1. Pagination Support: Generates swrFn that accepts pageParams from the key loader with proper type annotation extracted from GetterProp
  2. Function Wrapping: Removes the incorrect arrow function wrapper from swrKeyLoader to allow SWR to pass pagination arguments correctly
  3. Headers in useSWRInfinite: Includes header parameters in the swrFn between pageParams and options
  4. Headers in Mutations: Includes headers in mutation hook signatures, fetcher functions, and properly passes them to API calls

Before

// useSWRInfinite: swrFn doesn't accept parameters and drops headers
const swrFn = () => listPets(params, options);
const swrKeyLoader = () => (isEnabled ? getKeyLoader() : null);

// Mutations: fetchers don't include headers
export const getCreatePetsMutationFetcher = (
  params: CreatePetsParams,
  options?: AxiosRequestConfig,
) => {
  return (_: Key, { arg }: { arg: CreatePetsBody }) => {
    return createPets(arg, params, options); // Missing headers!
  };
};

After

// useSWRInfinite: Type safe pageParams with headers support
const swrFn = ([_url, pageParams]: [string, ListPetsParams & { page: number }]) =>
  listPets(pageParams, headers, options);
const swrKeyLoader = isEnabled ? getKeyLoader() : () => null;

// Mutations: headers properly included
export const getCreatePetsMutationFetcher = (
  params: CreatePetsParams,
  headers: CreatePetsHeaders,
  options?: AxiosRequestConfig,
) => {
  return (_: Key, { arg }: { arg: CreatePetsBody }) => {
    return createPets(arg, params, headers, options); // Headers included!
  };
};

Changes

1. Fix useSWRInfinite swrFn parameter handling

  • Separate path parameters (fixed values) from query parameters (pagination values)
  • Generate swrFn that accepts ([_url, pageParams]) from the key loader
  • Pass parameters correctly: operationName(pathParams, pageParams, options)

2. Add type annotation to pageParams

  • Extract type name from GetterProp.definition (e.g., "params: ListPetsParams""ListPetsParams")
  • Use never as fallback type for better type safety (causes compilation errors if params are missing when expected)
  • Enables IDE autocomplete and compile-time type checking

3. Fix swrKeyLoader function wrapping bug

  • The original implementation incorrectly wrapped the key loader: () => isEnabled ? getKeyLoader() : null
  • This prevented SWR from passing (pageIndex, previousPageData) arguments to the key loader
  • Fixed to return the actual key loader function: isEnabled ? getKeyLoader() : () => null
  • This allows proper pagination parameter handling in infinite queries

4. Fix headers in useSWRInfinite

  • Extract header parameters separately from other props
  • Include headers in swrFn generation between pageParams and httpRequestSecondArg
  • Ensures proper parameter order: operationName(pageParams, headers, options)

5. Fix headers in mutation fetchers and hooks

  • Include GetterPropType.HEADER in swrProps filter for mutation hooks
  • Include headers in swrMutationFetcherProperties for fetcher function parameters
  • Generate proper parameter lists for both GET and POST/PUT/DELETE mutations
  • Maintain correct parameter order: (params, headers, options) for fetcher signatures

6. Add comprehensive tests

  • Unit tests for query parameter type extraction logic
  • Unit tests for SWR builder functionality
  • Regression tests for type safety with strict TypeScript settings
  • Regression tests for swrKeyLoader function signature
  • Tests for headers in infinite queries, regular queries, and mutations
  • Test configuration with headers: true to catch header-related bugs

Testing

All tests pass successfully:

# Unit tests
yarn workspace @orval/swr test
# ✓ 5 tests passed

# Integration tests
yarn test:cli
# ✓ All generated code compiles with strict TypeScript

# Full test suite
yarn test:ci
# ✓ All tests pass

Affected Files

  • packages/swr/src/index.ts - Core implementation (pagination, headers, mutations)
  • packages/swr/src/client.test.ts - Unit tests for type extraction
  • packages/swr/src/index.test.ts - Unit tests for builder
  • packages/swr/vitest.config.ts - Test configuration
  • packages/swr/package.json - Added test script and vitest dependency
  • tests/regressions/swr.ts - Regression tests for type safety and headers
  • tests/configs/swr.config.ts - Added petstoreWithHeaders test configuration

Breaking Changes

None. This PR fixes bugs and adds support for features that were previously non-functional:

  • useSWRInfinite pagination was broken and unusable
  • Headers in mutation fetchers were ignored even when headers: true was configured

After regeneration, previously broken functionality will now work correctly.

Related Issues

Fixes #2106

…rom key

Previously, the generated swrFn for useSWRInfinite did not accept any
parameters from the key loader, causing pagination parameters to be
inaccessible. This fix:

- Separates path parameters (fixed) from query parameters (variable)
- Generates swrFn with (_url, pageParams) signature for infinite hooks
- Passes path params and page params separately to the operation function

This enables proper pagination support in useSWRInfinite hooks.
Previously, pageParams had 'any' type in the generated swrFn for
useSWRInfinite hooks. This change extracts the query parameter type
from the GetterProp definition and uses it as the type annotation.

Before:
  const swrFn = (_url: string, pageParams: any) => listPets(pageParams, options)

After:
  const swrFn = (_url: string, pageParams: ListPetsParams) => listPets(pageParams, options)

The fallback type is 'never' instead of 'any', which provides better
type safety by causing compilation errors if query params are missing
when expected.

This improves type safety and enables better IDE autocomplete for
pagination parameters.
The original implementation wrapped the keyLoader in an arrow function:
  () => isEnabled ? getKeyLoader() : null

This is incorrect because SWR calls swrKeyLoader with
(pageIndex, previousPageData) arguments, but the wrapper ignores them.

Fixed to return the actual keyLoader function directly when enabled:
  isEnabled ? getKeyLoader() : () => null

This allows SWR to properly pass pageIndex and previousPageData
to the key loader function.
Changed previousPageData from required to optional parameter in
useSWRInfinite keyLoader. This allows users to call keyLoader(0)
without explicitly passing undefined for the first page, which is
more ergonomic and complies with ESLint rules (unicorn/no-useless-undefined).
Fixed regression where headers were dropped in useSWRInfinite hooks.
The swrFn now correctly passes headers between pageParams and options:
- With query params: operationName(pathParams, pageParams, headers, options)
- Without query params: operationName(pathParams, headers, options)

Changes:
- Extract headerOnlyParams from props (filter by GetterPropType.HEADER)
- Pass headerOnlyParams to generateSwrImplementation
- Include headers in swrFn generation between pageParams and options

Note: This fix only applies to infinite hooks. A separate pre-existing bug
with mutation fetchers dropping headers remains to be addressed.
Add tests to verify type safety and proper parameter handling in useSWRInfinite:
- Unit tests for query parameter type extraction (client.test.ts)
- Regression tests for page: number type in pageParams
- Tests for headers in both infinite and regular hooks
- Test configuration with headers enabled (petstoreWithHeaders)

Tests verify:
- pageParams includes page: number type
- Headers are passed between pageParams and options
- Type safety without using 'as any' or type assertions
- Optional previousPageData parameter
- Query parameter type extraction logic
This fixes a bug where mutation fetchers and hooks were dropping
header parameters when headers: true was enabled in the config.

Changes:
- Add GetterPropType.HEADER to swrProps filter for mutation hooks
- Add GetterPropType.HEADER to swrMutationFetcherProperties
- Generate proper parameter lists for GET and POST/PUT/DELETE mutations
- Maintain correct parameter order: (params, headers, options)

Also adds comprehensive regression tests for:
- Mutation hooks with headers (useCreatePets)
- Mutation fetchers with headers (getCreatePetsMutationFetcher)
- Both headers: true and headers: false configurations
SWR passes the key array as a single argument to the fetcher,
not as spread arguments. The fetcher must destructure the array
to access individual elements.

Before (causes runtime error):
  const swrFn = (_url: string, pageParams: Type) => ...
  // _url receives the entire array, pageParams is undefined

After (correct):
  const swrFn = ([_url, pageParams]: [string, Type]) => ...
  // Properly destructures the array from SWR
Verifies that the key loader returns a properly typed tuple
and that TypeScript compilation succeeds with the array
destructuring pattern in swrFn.
@tk1024 tk1024 marked this pull request as ready for review November 24, 2025 12:17
@melloware melloware added this to the 8.0.0 milestone Nov 24, 2025
Replace || with ?? in query parameter type extraction to follow
@typescript-eslint/prefer-nullish-coalescing rule. This is a safer
operator that only coalesces on null/undefined, not falsy values.
@melloware melloware merged commit 82946b4 into orval-labs:master Nov 24, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SWR: useSWRInfinite implementation doesn't support pagination parameters

2 participants