Angular Query
Generate TanStack Query for Angular
Generate TanStack Query for Angular injectable functions from your OpenAPI specification.
Configuration
import { defineConfig } from 'orval';
export default defineConfig({
petstore: {
output: {
mode: 'tags-split',
target: 'src/petstore.ts',
schemas: 'src/model',
client: 'angular-query',
httpClient: 'angular',
mock: true,
},
input: {
target: './petstore.yaml',
},
},
});Generated Output
Orval generates injectable query functions using Angular's native HttpClient:
// Base operation function
export const showPetById = (
http: HttpClient,
petId: string,
options?: { signal?: AbortSignal | null },
): Promise<Pet> => {
const url = `/pets/${petId}`;
const request$ = http.get<Pet>(url);
if (options?.signal) {
return lastValueFrom(
request$.pipe(takeUntil(fromEvent(options.signal, 'abort'))),
);
}
return lastValueFrom(request$);
};
// Query key factory
export const getShowPetByIdQueryKey = (petId?: string) =>
[`/pets/${petId}`] as const;
// Injectable query function
export function injectShowPetById<TData = Pet, TError = unknown>(
petId: string | (() => string),
options?: { query?: Partial<CreateQueryOptions<Pet, TError, TData>> },
): CreateQueryResult<TData, TError> {
const http = inject(HttpClient);
const query = injectQuery(() => {
const _petId = typeof petId === 'function' ? petId() : petId;
return getShowPetByIdQueryOptions(http, _petId, options);
});
return query;
}Signal Reactivity
Parameters support Angular signals via getter functions:
@Component({...})
export class PetDetailComponent {
petId = signal('1');
// Query re-executes when petId() changes
pet = injectShowPetById(() => this.petId());
// Dynamic enabled/disabled
conditionalPet = injectShowPetById(
() => this.petId(),
() => ({ query: { enabled: this.isEnabled() } }),
);
}Mutations
For POST, PUT, PATCH, DELETE operations, Orval generates mutation options factories and injectable mutation functions:
// Mutation options factory
export const getCreatePetsMutationOptions = <TError = Error, TContext = unknown>(
http: HttpClient,
queryClient: QueryClient,
options?: {
mutation?: CreateMutationOptions<
Awaited<ReturnType<typeof createPets>>,
TError,
{ data: CreatePetsBody },
TContext
>;
fetch?: RequestInit;
},
) => {
const mutationKey = ['createPets'];
const mutationFn = async (props: { data: CreatePetsBody }) => {
return createPets(http, props.data, options?.fetch);
};
return { mutationKey, mutationFn, ...options?.mutation };
};
// Injectable mutation function
export const injectCreatePets = <TError = Error, TContext = unknown>(
options?: {
mutation?: CreateMutationOptions<...>;
fetch?: RequestInit;
},
): CreateMutationResult<...> => {
const http = inject(HttpClient);
const queryClient = inject(QueryClient);
const mutationOptions = getCreatePetsMutationOptions(http, queryClient, options);
return injectMutation(() => mutationOptions);
};Usage:
@Component({...})
export class CreatePetComponent {
createPet = injectCreatePets();
onSubmit(pet: CreatePetsBody) {
this.createPet.mutate({ data: pet });
}
}Query Invalidation
When useInvalidate: true is set, Orval generates helper functions to invalidate queries:
export const invalidateShowPetById = async (
queryClient: QueryClient,
petId: string,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getShowPetByIdQueryKey(petId) },
options,
);
return queryClient;
};Set Query Data
When useSetQueryData: true is set, Orval generates type-safe helper functions to update cached query data:
export const setShowPetByIdQueryData = (
queryClient: QueryClient,
petId: string,
updater:
| Awaited<ReturnType<typeof showPetById>>
| undefined
| ((
old: Awaited<ReturnType<typeof showPetById>> | undefined,
) => Awaited<ReturnType<typeof showPetById>> | undefined),
) => {
queryClient.setQueryData(getShowPetByIdQueryKey(petId), updater);
};Automatic Mutation Invalidation
Configure automatic invalidation when mutations succeed:
import { defineConfig } from 'orval';
export default defineConfig({
petstore: {
output: {
client: 'angular-query',
override: {
query: {
useInvalidate: true,
mutationInvalidates: [
{
onMutations: ['createPets'],
invalidates: ['listPets'],
},
{
onMutations: ['deletePet', 'updatePet'],
invalidates: [
'listPets',
{ query: 'showPetById', params: ['petId'] },
],
},
],
},
},
},
},
});Usage:
@Component({
template: `
<ul>
@for (pet of pets.data(); track pet.id) {
<li>{{ pet.name }} <button (click)="onDelete(pet.id)">Delete</button></li>
}
</ul>
`,
})
export class PetListComponent {
pets = injectListPets();
deletePet = injectDeletePet();
onDelete(petId: string) {
// listPets and showPetById(petId) are automatically invalidated
this.deletePet.mutate({ petId });
}
}Composing with user callbacks
When you provide your own onSuccess, both the auto-invalidation and your callback run:
deletePet = injectDeletePet({
mutation: {
onSuccess: () => this.snackbar.open('Pet deleted!'),
},
// invalidation still runs automatically before onSuccess
});Skipping auto-invalidation
Pass skipInvalidation: true to opt out of auto-invalidation at runtime — for example when you need to delay or conditionally run invalidation yourself:
deletePet = injectDeletePet({
mutation: {
onSuccess: (_data, variables) => {
// Custom delayed invalidation
setTimeout(() => {
this.queryClient.invalidateQueries({
queryKey: getListPetsQueryKey(),
});
}, 700);
},
},
skipInvalidation: true,
});Query Options
Configure infinite queries and default options:
import { defineConfig } from 'orval';
export default defineConfig({
petstore: {
output: {
override: {
query: {
useQuery: true,
useInfinite: true,
useInfiniteQueryParam: 'nextId',
options: {
staleTime: 10000,
},
},
},
},
},
});Per-Operation Configuration
You can override the query options for a single operation or tag:
import { defineConfig } from 'orval';
export default defineConfig({
petstore: {
output: {
override: {
operations: {
listPets: {
query: {
useInfinite: true,
useInfiniteQueryParam: 'cursor',
options: {
staleTime: 60000,
},
},
},
},
},
},
},
});Zod Runtime Validation
When using Zod schemas, you can enable automatic runtime validation of HTTP responses. Orval will pipe each response through Schema.parse() inside the RxJS Observable pipeline before converting to a Promise:
import { defineConfig } from 'orval';
export default defineConfig({
petstore: {
output: {
client: 'angular-query',
httpClient: 'angular',
schemas: { path: 'src/model', type: 'zod' },
override: {
query: {
runtimeValidation: true,
},
},
},
input: {
target: './petstore.yaml',
},
},
});This generates:
const request$ = http.get<Pets>(url, { params: httpParams })
.pipe(map(data => Pets.parse(data)));Validation is automatically skipped for primitive response types (string, void, etc.) and for operations using a custom mutator.
Full Example
See the Angular Query sample on GitHub.