Aviary
Client

TanStack Query

queryOptions / mutationOptions from your framework adapter.

TanStack Query is an extension, not a core flag. By default the client is a plain typed fetch with no TanStack dependency. Install the extension and register it, and each endpoint returns a handle exposing TanStack's queryOptions/mutationOptions helpers — GET routes get queryOptions(), everything else mutationOptions().

pnpm add -D @dudousxd/nestjs-codegen-tanstack
src/app.module.ts
import { tanstackQuery } from '@dudousxd/nestjs-codegen-tanstack';

NestjsCodegenModule.forRoot({
  contracts: { glob: 'src/**/*.controller.ts' },
  codegen: { outDir: 'src/generated' },
  extensions: [tanstackQuery()],
});

Which package?

You don't install @tanstack/query-core directly (nobody does) — your framework adapter re-exports the helpers. Point the extension's import at the package you already have (@tanstack/react-query is the default):

tanstackQuery(); // import defaults to '@tanstack/react-query'
tanstackQuery({ import: '@tanstack/vue-query' });
tanstackQuery({ import: '@tanstack/svelte-query' });
tanstackQuery({ import: '@tanstack/solid-query' });

Usage

The leaf is still awaitableawait api.users.list() does a plain request — and the same handle exposes the TanStack helpers:

import { useQuery, useMutation, useInfiniteQuery, useQueryClient } from '@tanstack/react-query';
import { api } from '../lib/api';

function Users() {
  const qc = useQueryClient();
  const list = useQuery(api.users.list().queryOptions());
  const pages = useInfiniteQuery(api.users.list().infiniteQueryOptions());
  const create = useMutation({
    ...api.users.create().mutationOptions(),
    onSuccess: () => qc.invalidateQueries({ queryKey: api.users.list().queryKey() }),
  });
  // …
}

infiniteQueryOptions() (GET routes) adds page to the query and reads the next page from response.meta.page / response.meta.lastPage.

Each handle builds a real queryOptions/mutationOptions object, so you compose it with your own onSuccess, select, staleTime, etc. — and queryKey() derives a stable key from the route name + input for you.

On this page