OrvalOrval

Vue Query

Generate type-safe Vue Query composables from OpenAPI

Generate fully typed TanStack Query for Vue composables from your OpenAPI specification.

Configuration

Set the client option to vue-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: 'vue-query',
      mock: true,
    },
    input: {
      target: './petstore.yaml',
    },
  },
});

Generated Output

Orval generates one composable per path in your OpenAPI specification. For example, this specification generates:

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 useShowPetById = <
  TData = AsyncReturnType<typeof showPetById>,
  TError = Error,
>(
  petId: string,
  options?: {
    query?: UseQueryOptions<AsyncReturnType<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 = useQuery<AsyncReturnType<typeof queryFn>, TError, TData>(
    queryKey,
    queryFn,
    {
      enabled: petId !== null && petId !== undefined,
      ...queryOptions,
    },
  );

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

Reactive Parameters

Generated composables accept reactive parameters so the query re-runs when an input changes. On Vue Query v5 (which requires Vue 3.3+), every parameter is typed as MaybeRefOrGetter<T>

  • that is T | Ref<T> | (() => T) - and is unwrapped internally with toValue(). This lets you pass the getter directly, with no computed wrapper:
// All three forms work:
useShowPetById(petId.value);
useShowPetById(petId); 
useShowPetById(() => props.petId);

MaybeRefOrGetter<T> is a superset of MaybeRef<T>, so existing call sites that pass refs or plain values keep working unchanged. Projects generating against Vue Query v4 (or the legacy vue-query v3 package) continue to use MaybeRef<T>/unref(), since those targets may run on Vue versions older than 3.3.

Infinite Queries

Generate useInfiniteQuery composables:

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

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

Per-Operation Overrides

Override query options for specific operations:

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

export default defineConfig({
  petstore: {
    output: {
      client: 'vue-query',
      override: {
        operations: {
          listPets: {
            query: {
              useInfinite: true,
            },
          },
        },
      },
    },
    input: {
      target: './petstore.yaml',
    },
  },
});

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: MaybeRefOrGetter<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 Vue Query example on GitHub.

On this page