OrvalOrval

Solid Query

Generate type-safe Solid Query primitives from OpenAPI

Generate fully typed TanStack Query for Solid primitives from your OpenAPI specification.

Configuration

Set the client option to solid-query:

orval.config.ts
import { defineConfig } from 'orval';

export default defineConfig({
  petstore: {
    output: {
      mode: 'tags-split',
      target: 'src/api/petstore.ts',
      schemas: 'src/api/model',
      client: 'solid-query',
      mock: true,
    },
    input: {
      target: './petstore.yaml',
    },
  },
});

Generated Output

Orval generates one primitive per path using @tanstack/solid-query. For example:

export const showPetById = (
  petId: string,
  options?: AxiosRequestConfig,
): Promise<AxiosResponse<Pet>> => {
  return axios.get(`/pets/${petId}`, options);
};

export const getShowPetByIdQueryKey = (petId: string) => [`/pets/${petId}`];

export const createShowPetById = <
  TData = Awaited<ReturnType<typeof showPetById>>,
  TError = Error,
>(
  petId: string,
  options?: {
    query?: CreateQueryOptions<
      Awaited<ReturnType<typeof showPetById>>,
      TError,
      TData
    >;
    axios?: AxiosRequestConfig;
  },
) => {
  const { query: queryOptions, axios: axiosOptions } = options ?? {};

  const queryKey = queryOptions?.queryKey ?? getShowPetByIdQueryKey(petId);
  const queryFn = () => showPetById(petId, axiosOptions);

  const query = createQuery<Awaited<ReturnType<typeof queryFn>>, TError, TData>(
    () => ({
      queryKey,
      queryFn,
      enabled: petId !== null && petId !== undefined,
      ...queryOptions,
    }),
  );

  return {
    queryKey,
    ...query,
  };
};

Key Differences from React Query

Solid Query uses the create prefix instead of use:

  • createQuery instead of useQuery
  • createMutation instead of useMutation
  • createInfiniteQuery instead of useInfiniteQuery

The generated primitives follow SolidJS conventions and work seamlessly with Solid's reactivity system.

Infinite Queries

Generate infinite query primitives:

orval.config.ts
import { defineConfig } from 'orval';

export default defineConfig({
  petstore: {
    output: {
      client: 'solid-query',
      override: {
        query: {
          useQuery: true,
          useInfinite: true,
          useInfiniteQueryParam: 'nextId',
          options: {
            staleTime: 10000,
          },
        },
      },
    },
    input: {
      target: './petstore.yaml',
    },
  },
});

TanStack Query requires initialPageParam and getNextPageParam on every infinite query, and orval cannot infer either of them. Solid Query is generated without the as cast the other frameworks use, so the generated primitive asks the caller for them instead of asserting over the gap:

const pets = createListPetsInfinite(params, {
  query: {
    initialPageParam: undefined,
    getNextPageParam: (lastPage) => lastPage.nextId,
  },
});

Supply them once in override.query.options instead, and they are baked into the generated primitive — the options argument then goes back to being optional:

options: {
  initialPageParam: undefined,
  getNextPageParam: (lastPage) => lastPage.nextId,
},

initialData is not accepted on the query object. Pass it through the generated primitive's own overload instead, which is what preserves the distinction between data being defined and possibly undefined.

SolidStart vs Solid Query

If you're using SolidStart, consider using the SolidStart client instead, which uses native Solid Router primitives (query() and action()) for better SSR integration.

Set Query Data

When useSetQueryData: true is set, Orval generates type-safe helper functions to update cached query data:

export const setListPetsQueryData = (
  queryClient: QueryClient,
  params: ListPetsParams | undefined,
  updater:
    | Awaited<ReturnType<typeof listPets>>
    | undefined
    | ((
        old: Awaited<ReturnType<typeof listPets>> | undefined,
      ) => Awaited<ReturnType<typeof listPets>> | undefined),
  $exactMatch: boolean = true,
) => {
  queryClient.setQueriesData<Awaited<ReturnType<typeof listPets>>>(
    { exact: $exactMatch, queryKey: getListPetsQueryKey(params) },
    updater,
  );
};

The helper uses setQueriesData so query keys can be matched by prefix. Pass $exactMatch: false to match and update every cached entry sharing the given path prefix. For endpoints with query params or a body, those args are widened to accept undefined — pass undefined (together with $exactMatch: false) to update every cached entry sharing the same path; the updater is invoked once per matched entry.

Prior to 8.11.0, the helper called setQueryData, which writes (and creates) a single exact-key entry. Since 8.11.0 the helper calls setQueriesData, which only updates entries that already exist (it never creates one), and as of this change it matches the key exactly by default. Pass $exactMatch: false to match by prefix instead. Review existing call sites when upgrading to confirm they still behave as expected.

Get Query Data

When useGetQueryData: true is set, Orval generates type-safe helper functions to read cached query data:

export const getListPetsQueryData = (
  queryClient: QueryClient,
  params: ListPetsParams,
) =>
  queryClient.getQueryData<Awaited<ReturnType<typeof listPets>>>(
    getListPetsQueryKey(params),
  );

Full Example

See the complete Solid Query example on GitHub.

On this page