Skip to content

Commit 31bac35

Browse files
authored
chore (doc) update documentation for angular query (orval-labs#2784)
1 parent 65cc8a0 commit 31bac35

File tree

2 files changed

+244
-18
lines changed

2 files changed

+244
-18
lines changed

docs/src/pages/guides/angular-query.mdx

Lines changed: 202 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export default defineConfig({
1717
target: 'src/petstore.ts',
1818
schemas: 'src/model',
1919
client: 'angular-query',
20+
httpClient: 'angular',
2021
mock: true,
2122
},
2223
input: {
@@ -33,12 +34,14 @@ The Angular Query mode will generate an implementation file with one injectable
3334
For example, <a href="https://github.com/orval-labs/orval/blob/master/samples/angular-query/petstore.yaml" target="_blank">this Swagger specification</a> will generate the following:
3435

3536
```ts
37+
// Base operation function
3638
export const showPetById = (
3739
http: HttpClient,
3840
petId: string,
39-
options?: { signal?: AbortSignal },
41+
options?: { signal?: AbortSignal | null },
4042
): Promise<Pet> => {
41-
const request$ = http.get<Pet>(`/pets/${petId}`);
43+
const url = `/pets/${petId}`;
44+
const request$ = http.get<Pet>(url);
4245
if (options?.signal) {
4346
return lastValueFrom(
4447
request$.pipe(takeUntil(fromEvent(options.signal, 'abort'))),
@@ -47,22 +50,26 @@ export const showPetById = (
4750
return lastValueFrom(request$);
4851
};
4952

50-
export const getShowPetByIdQueryKey = (petId: string) => [`/pets/${petId}`];
53+
// Query key factory
54+
export const getShowPetByIdQueryKey = (petId?: string) => {
55+
return [`/pets/${petId}`] as const;
56+
};
5157

58+
// Query options factory
5259
export const getShowPetByIdQueryOptions = <
5360
TData = Awaited<ReturnType<typeof showPetById>>,
5461
TError = unknown,
5562
>(
63+
http: HttpClient,
5664
petId: string,
5765
options?: {
5866
query?: Partial<
5967
CreateQueryOptions<Awaited<ReturnType<typeof showPetById>>, TError, TData>
6068
>;
61-
fetch?: { signal?: AbortSignal };
69+
fetch?: RequestInit;
6270
},
6371
) => {
6472
const { query: queryOptions, fetch: fetchOptions } = options ?? {};
65-
const http = inject(HttpClient);
6673

6774
const queryKey = queryOptions?.queryKey ?? getShowPetByIdQueryKey(petId);
6875

@@ -82,29 +89,206 @@ export const getShowPetByIdQueryOptions = <
8289
>;
8390
};
8491

85-
export const injectShowPetById = <
92+
// Injectable query function
93+
export function injectShowPetById<
8694
TData = Awaited<ReturnType<typeof showPetById>>,
8795
TError = unknown,
8896
>(
89-
petId: string,
97+
petId: string | (() => string),
98+
options?:
99+
| {
100+
query?: Partial<
101+
CreateQueryOptions<
102+
Awaited<ReturnType<typeof showPetById>>,
103+
TError,
104+
TData
105+
>
106+
>;
107+
fetch?: RequestInit;
108+
}
109+
| (() => {
110+
query?: Partial<
111+
CreateQueryOptions<
112+
Awaited<ReturnType<typeof showPetById>>,
113+
TError,
114+
TData
115+
>
116+
>;
117+
fetch?: RequestInit;
118+
}),
119+
): CreateQueryResult<TData, TError> {
120+
const http = inject(HttpClient);
121+
122+
const query = injectQuery(() => {
123+
const _petId = typeof petId === 'function' ? petId() : petId;
124+
const _options = typeof options === 'function' ? options() : options;
125+
return getShowPetByIdQueryOptions(http, _petId, _options);
126+
}) as CreateQueryResult<TData, TError>;
127+
128+
return query;
129+
}
130+
```
131+
132+
## Signal Reactivity
133+
134+
The generated `inject*` functions support Angular signals out of the box. Parameters can be passed as **getter functions**, enabling reactive query updates when signals change:
135+
136+
```ts
137+
@Component({...})
138+
export class PetDetailComponent {
139+
// Signal-based parameter
140+
petId = signal('1');
141+
142+
// Query automatically re-executes when petId() changes
143+
pet = injectShowPetById(() => this.petId());
144+
145+
// Also works with options for dynamic enabled/disabled
146+
conditionalPet = injectShowPetById(
147+
() => this.petId(),
148+
() => ({
149+
query: { enabled: this.isEnabled() },
150+
}),
151+
);
152+
}
153+
```
154+
155+
This pattern works for all parameters including query params and options.
156+
157+
## Mutations
158+
159+
For POST, PUT, PATCH, and DELETE operations, Orval generates mutation functions:
160+
161+
```ts
162+
// Mutation options factory
163+
export const getCreatePetsMutationOptions = <TError = Error, TContext = unknown>(
164+
http: HttpClient,
165+
queryClient: QueryClient,
90166
options?: {
91-
query?: Partial<
92-
CreateQueryOptions<Awaited<ReturnType<typeof showPetById>>, TError, TData>
93-
>;
94-
fetch?: { signal?: AbortSignal };
167+
mutation?: CreateMutationOptions<...>;
168+
fetch?: RequestInit;
95169
},
96-
) => {
97-
const queryOptions = getShowPetByIdQueryOptions(petId, options);
170+
) => { ... };
98171

99-
const query = injectQuery(() => queryOptions) as CreateQueryResult<
100-
TData,
101-
TError
102-
>;
172+
// Injectable mutation function
173+
export const injectCreatePets = <TError = Error, TContext = unknown>(
174+
options?: {...}
175+
): CreateMutationResult<...> => {
176+
const http = inject(HttpClient);
177+
const queryClient = inject(QueryClient);
178+
// ...
179+
return injectMutation(() => mutationOptions);
180+
};
181+
```
103182

104-
return query;
183+
Usage:
184+
185+
```ts
186+
@Component({...})
187+
export class CreatePetComponent {
188+
createPet = injectCreatePets();
189+
190+
onSubmit(pet: CreatePetsBody) {
191+
this.createPet.mutate({ data: pet });
192+
}
193+
}
194+
```
195+
196+
## Query Invalidation
197+
198+
When `useInvalidate: true` is set, Orval generates helper functions to invalidate queries:
199+
200+
```ts
201+
export const invalidateShowPetById = async (
202+
queryClient: QueryClient,
203+
petId: string,
204+
options?: InvalidateOptions,
205+
): Promise<QueryClient> => {
206+
await queryClient.invalidateQueries(
207+
{ queryKey: getShowPetByIdQueryKey(petId) },
208+
options,
209+
);
210+
return queryClient;
105211
};
106212
```
107213

214+
### Automatic Mutation Invalidation (Angular Query Only)
215+
216+
The Angular Query client supports declarative, automatic query invalidation when mutations succeed via the `mutationInvalidates` config:
217+
218+
```js
219+
import { defineConfig } from 'orval';
220+
221+
export default defineConfig({
222+
petstore: {
223+
output: {
224+
client: 'angular-query',
225+
override: {
226+
query: {
227+
useInvalidate: true,
228+
mutationInvalidates: [
229+
{
230+
onMutations: ['createPets'],
231+
invalidates: ['listPets'],
232+
},
233+
{
234+
onMutations: ['deletePet', 'updatePet', 'patchPet'],
235+
invalidates: [
236+
'listPets',
237+
{ query: 'showPetById', params: ['petId'] },
238+
],
239+
},
240+
],
241+
},
242+
},
243+
},
244+
// ...
245+
},
246+
});
247+
```
248+
249+
This generates an `onSuccess` handler in the mutation options:
250+
251+
```ts
252+
const onSuccess = (data, variables, onMutateResult, context) => {
253+
queryClient.invalidateQueries({ queryKey: getListPetsQueryKey() });
254+
queryClient.invalidateQueries({
255+
queryKey: getShowPetByIdQueryKey(variables.petId),
256+
});
257+
mutationOptions?.onSuccess?.(data, variables, onMutateResult, context);
258+
};
259+
```
260+
261+
**Param-based invalidation**: With `{ query: 'showPetById', params: ['petId'] }`, when `deletePet({ petId: '123' })` succeeds, only the query for pet `123` is invalidated—not all `showPetById` queries.
262+
263+
**Usage**: To get automatic invalidation, use the generated `inject*` mutation functions:
264+
265+
```ts
266+
@Component({
267+
template: `
268+
<ul>
269+
@for (pet of pets.data(); track pet.id) {
270+
<li>
271+
{{ pet.name }}
272+
<button (click)="onDelete(pet.id)">Delete</button>
273+
</li>
274+
}
275+
</ul>
276+
`,
277+
})
278+
export class PetListComponent {
279+
// Query - will be automatically invalidated after mutations
280+
pets = injectListPets();
281+
282+
// Mutation - automatic invalidation is built into the generated options
283+
deletePet = injectDeletePet();
284+
285+
onDelete(petId: string) {
286+
// After success, listPets and showPetById(petId) are automatically invalidated
287+
this.deletePet.mutate({ petId });
288+
}
289+
}
290+
```
291+
108292
## How to Use Other Queries
109293

110294
Given the following configuration, Orval will generate query and infinite query functions with a `nextId` query parameter. It is also possible to override the config for every query with the `options` field.

docs/src/pages/reference/configuration/output.mdx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,6 +1262,48 @@ export const invalidateShowPetById = async (
12621262
}
12631263
```
12641264

1265+
#### mutationInvalidates
1266+
1267+
Type: `Array<{ onMutations: string[], invalidates: (string | { query: string, params: string[] })[] }>`.
1268+
1269+
**Angular Query only.** Declaratively invalidate queries when mutations succeed. Requires `useInvalidate: true`.
1270+
1271+
```js
1272+
import { defineConfig } from 'orval';
1273+
1274+
export default defineConfig({
1275+
petstore: {
1276+
output: {
1277+
client: 'angular-query',
1278+
override: {
1279+
query: {
1280+
useInvalidate: true,
1281+
mutationInvalidates: [
1282+
{
1283+
onMutations: ['createPets'],
1284+
invalidates: ['listPets'],
1285+
},
1286+
{
1287+
onMutations: ['deletePet', 'updatePet'],
1288+
invalidates: [
1289+
'listPets',
1290+
{ query: 'showPetById', params: ['petId'] },
1291+
],
1292+
},
1293+
],
1294+
},
1295+
},
1296+
},
1297+
},
1298+
});
1299+
```
1300+
1301+
##### Param-based invalidation
1302+
1303+
With `{ query: 'showPetById', params: ['petId'] }`, the mutation's variables are used to determine which specific query to invalidate. When `deletePet({ petId: '123' })` succeeds, only `showPetById('123')` is invalidated—not all `showPetById` queries.
1304+
1305+
See the [Angular Query guide](../../guides/angular-query#automatic-mutation-invalidation-angular-query-only) for more details.
1306+
12651307
#### useInfiniteQueryParam
12661308

12671309
Type: `String`.

0 commit comments

Comments
 (0)