2
June 18, 2026 · View on GitHub
This guide covers every pattern you'll encounter when using generated tokens in Angular components: plain GET, conditional requests, path params, header params, mutations, and security tokens.
All examples use the Petstore data-access lib generated in Guide 1.
The basic pattern
Every generated token's factory returns a function. You call that function once — usually
in the component field initializer — and it returns an HttpResourceRef<T>:
@Component({ ... })
export class PetsPageComponent {
private findPetsByStatus = inject(FIND_PETS_BY_STATUS);
readonly pets = this.findPetsByStatus({ status: 'available' });
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
// plain params object — fires immediately
}
@if (pets.isLoading()) {
<mat-progress-bar mode="indeterminate" />
}
@for (pet of pets.value() ?? []; track pet.id) {
<p>{{ pet.name }} — {{ pet.status }}</p>
}
@if (pets.error()) {
<p class="error">Failed to load pets</p>
}
The resource signals — .value(), .isLoading(), .error() — integrate directly with
@if / @for. No subscription or manual change detection needed.
Reactive params (thunk pattern)
When params depend on a signal, pass a thunk — a function that returns the params.
httpResource re-fires the request automatically whenever any signal inside the thunk changes:
@Component({ ... })
export class PetsPageComponent {
private findPetsByStatus = inject(FIND_PETS_BY_STATUS);
readonly statusFilter = signal<'available' | 'pending' | 'sold'>('available');
// Re-fetches whenever statusFilter() changes
readonly pets = this.findPetsByStatus(() => ({ status: this.statusFilter() }));
}
<mat-chip-listbox [(value)]="statusFilter">
<mat-chip-option value="available">Available</mat-chip-option>
<mat-chip-option value="pending">Pending</mat-chip-option>
<mat-chip-option value="sold">Sold</mat-chip-option>
</mat-chip-listbox>
Conditional requests — suppressing a request
Return undefined from the thunk to keep the resource idle (no HTTP request).
The resource re-fires the moment the thunk starts returning a value:
@Component({ ... })
export class SearchComponent {
private youtubeFn = inject(YOUTUBE_SEARCH_LIST);
readonly query = signal('');
readonly apiKey = signal<string | null>(null);
// No request until both query and apiKey are set
readonly results = this.youtubeFn(() =>
this.query() && this.apiKey()
? { q: this.query(), key: this.apiKey()!, part: 'snippet', maxResults: 10 }
: undefined
);
}
When undefined is returned the resource stays in 'idle' status — .isLoading() is
false and .value() is undefined. No need to null-guard the URL string or use
switchMap.
Path params
Path params (/pet/{petId}) become required positional arguments on the returned function.
Provide them directly — the generator interpolates them into the URL inside the reactive lambda:
@Component({ ... })
export class PetDetailComponent {
private getPetById = inject(GET_PET_BY_ID);
readonly petId = input.required<string>();
// Path param as plain value — re-fetches if petId input changes
readonly pet = this.getPetById(this.petId);
// ^^^^^^^^^^^^
// can be a plain value or a signal
}
When the path param itself comes from a signal (or input()) and you also need optional
query params, use a thunk:
readonly pet = this.getPetById(() => this.petId());
Header params
in: header parameters (e.g. X-Api-Version) become named string arguments placed after
path params in the generated function signature:
// Generated signature (example):
// (xApiVersion: string, acceptLanguage?: string, params?: ListReportsParams)
readonly reports = this.listReports('2024-01', 'en-GB', { page: 1, limit: 20 });
Optional header params use conditional spreading inside the resource lambda — no header is
sent when you pass undefined.
Mutations (POST / PUT / PATCH / DELETE)
Mutation tokens have a slightly different factory signature: the returned function accepts a
body as its first argument (after any path params). You can pass a plain value or a signal.
JSON body
@Component({ ... })
export class AddPetComponent {
private addPet = inject(ADD_PET);
readonly formValue = signal<AddPetBody>({
name: '',
status: 'available',
photoUrls: [],
});
// Resource re-posts whenever formValue() changes
readonly result = this.addPet(this.formValue);
}
For a button-triggered mutation, use a WritableSignal and set it on click:
readonly pendingPet = signal<AddPetBody | null>(null);
// Suppress the request until the user clicks Submit
readonly result = this.addPet(() => this.pendingPet() ?? undefined);
onSubmit(pet: AddPetBody): void {
this.pendingPet.set(pet);
}
multipart/form-data body
The body type from the spec describes field shapes. Angular's HttpClient requires an
actual FormData at runtime — cast with as unknown as BodyType:
@Component({ ... })
export class UploadComponent {
private uploadFile = inject(UPLOAD_FILE);
readonly selectedFile = signal<File | null>(null);
private readonly formData = computed(() => {
const file = this.selectedFile();
if (!file) return null;
const fd = new FormData();
fd.append('file', file);
return fd as unknown as UploadFileBody;
});
readonly upload = this.uploadFile('42', () => this.formData() ?? undefined);
// ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// petId (path param) body thunk (idle when null)
}
Security tokens
Bearer / OAuth2
When the spec declares a bearerAuth or oauth2 scheme, the generator emits an
InjectionToken<Signal<string | null>>:
// oauth2.security-token.ts (generated)
export const OAUTH2 = new InjectionToken<Signal<string | null>>('OAUTH2');
Each endpoint token injects it optionally and reads it inside the reactive lambda:
// Inside the generated httpResource lambda (simplified):
const oauth2 = inject(OAUTH2, { optional: true });
headers: {
...(oauth2?.() != null ? { Authorization: `Bearer ${oauth2()}` } : {}),
},
Because the signal is read inside the lambda, changing the token value automatically re-fires any resource that uses it — no manual reload needed.
To wire it up, provide any Signal<string | null>:
// app.config.ts
import { signal } from '@angular/core';
import { OAUTH2 } from '@myapp/petstore-data-access';
export const ACCESS_TOKEN = new InjectionToken<WritableSignal<string | null>>(
'ACCESS_TOKEN',
{ providedIn: 'root', factory: () => signal(null) },
);
// providers array:
{ provide: OAUTH2, useFactory: () => inject(ACCESS_TOKEN) }
Then in your auth service (or wherever you receive tokens):
@Component({ ... })
export class LoginComponent {
private token = inject(ACCESS_TOKEN);
onLoginSuccess(accessToken: string): void {
this.token.set(accessToken);
// Every resource that reads OAUTH2 re-fires automatically
}
onLogout(): void {
this.token.set(null);
}
}
API key (header / query)
Same pattern — InjectionToken<Signal<string | null>>. The generated lambda puts the key
in the appropriate header or query param:
export const API_KEY = new InjectionToken<Signal<string | null>>('API_KEY');
// In providers:
{ provide: API_KEY, useFactory: () => inject(MY_API_KEY_SIGNAL) }
Sharing a resource between components
Each call to the factory function creates an independent httpResource instance — two
components calling this.findPetsByStatus(...) fire two separate HTTP requests.
To share, hoist the resource into a root-scoped token:
// pets.store.ts
export const PETS_STORE = new InjectionToken<{
statusFilter: WritableSignal<'available' | 'pending' | 'sold'>;
pets: ReturnType<typeof httpResource<FindPetsByStatusResponse>>;
}>('PETS_STORE', {
providedIn: 'root',
factory: () => {
const findPetsByStatus = inject(FIND_PETS_BY_STATUS);
const statusFilter = signal<'available' | 'pending' | 'sold'>('available');
return {
statusFilter,
pets: findPetsByStatus(() => ({ status: statusFilter() })),
};
},
});
Components then inject PETS_STORE — one HTTP request, many consumers.