### Install Dependencies and Start Development Server Source: https://github.com/ngneat/query/blob/main/CONTRIBUTING.md Run these commands to set up your local development environment and start the project. ```bash npm i ``` ```bash npm start ``` -------------------------------- ### Server-Side Rendering with Dehydration/Hydration Source: https://context7.com/ngneat/query/llms.txt On the server, provide a fresh QueryClient, dehydrate state into the HTML, then hydrate on the client before bootstrap. This example demonstrates the server-side setup for rendering and the client-side setup for hydration. ```typescript // server.ts import { renderApplication } from '@angular/platform-server'; import { provideQueryClient } from '@ngneat/query'; import { QueryClient, dehydrate } from '@tanstack/query-core'; async function handleRequest(req: Request, res: Response) { const queryClient = new QueryClient(); let html = await renderApplication(AppComponent, { providers: [provideQueryClient(queryClient)], }); const state = JSON.stringify(dehydrate(queryClient)); html = html.replace('', ``); res.send(html); queryClient.clear(); } ``` ```typescript // main.ts (browser) import { bootstrapApplication, BrowserModule } from '@angular/platform-browser'; import { importProvidersFrom } from '@angular/core'; import { provideQueryClient } from '@ngneat/query'; import { QueryClient, hydrate } from '@tanstack/query-core'; const queryClient = new QueryClient(); hydrate(queryClient, JSON.parse((window as any).__QUERY_STATE__)); bootstrapApplication(AppComponent, { providers: [ importProvidersFrom(BrowserModule.withServerTransition({ appId: 'my-app' })), provideQueryClient(queryClient), ], }); ``` -------------------------------- ### Install @ngneat/query and @tanstack/query-core Source: https://context7.com/ngneat/query/llms.txt Install the necessary packages using npm. ```bash npm install @ngneat/query @tanstack/query-core ``` -------------------------------- ### Install @ngneat/query Source: https://github.com/ngneat/query/blob/main/README.md Install the package using npm. Ensure @tanstack/query-core is also installed for full functionality. ```bash npm i @ngneat/query ``` -------------------------------- ### Provide Query DevTools in Development Source: https://github.com/ngneat/query/blob/main/devtools/README.md Install and import `provideQueryDevTools` to enable devtools. Use this only in development environments by conditionally providing it based on `environment.production`. ```typescript import { provideQueryDevTools } from '@ngneat/query'; import { environment } from 'src/environments/environment'; bootstrapApplication(AppComponent, { providers: [environment.production ? [] : provideQueryDevTools(options)], }); ``` -------------------------------- ### Example Commit Message: Documentation Update Source: https://github.com/ngneat/query/blob/main/CONTRIBUTING.md This is an example of a commit message for updating documentation. ```git docs(changelog): update changelog to beta.5 ``` -------------------------------- ### Track Application Fetching State (Signal) Source: https://github.com/ngneat/query/blob/main/README.md Use injectIsFetching to get the number of queries currently loading or fetching. This signal example shows how to track overall fetching and fetching for specific query keys. ```typescript import { injectIsFetching } from '@ngneat/query'; class TodoComponent { #isFetching = injectIsFetching(); // How many queries overall are currently fetching data? public isFetching = this.#isFetching().result; // How many queries matching the todos prefix are currently fetching? public isFetchingTodos = this.#isFetching({ queryKey: ['todos'], }).result; } ``` -------------------------------- ### Track Application Fetching State (Observable) Source: https://github.com/ngneat/query/blob/main/README.md Use injectIsFetching to get the number of queries currently loading or fetching. This observable example shows how to track overall fetching and fetching for specific query keys. ```typescript import { injectIsFetching } from '@ngneat/query'; class TodoComponent { #isFetching = injectIsFetching(); // How many queries overall are currently fetching data? public isFetching$ = this.#isFetching().result$; // How many queries matching the todos prefix are currently fetching? public isFetchingTodos$ = this.#isFetching({ queryKey: ['todos'] }).result$; } ``` -------------------------------- ### Track Application Mutating State (Signal) Source: https://github.com/ngneat/query/blob/main/README.md Use injectIsMutating to get the number of mutations currently fetching. This signal example shows how to track overall mutating and mutating for specific query keys. ```typescript import { injectIsMutating } from '@ngneat/query'; class TodoComponent { #isMutating = injectIsMutating(); // How many queries overall are currently fetching data? public isFetching = this.#isMutating().result; // How many queries matching the todos prefix are currently fetching? public isFetchingTodos = this.#isMutating({ queryKey: ['todos'], }).result; } ``` -------------------------------- ### Track Application Mutating State (Observable) Source: https://github.com/ngneat/query/blob/main/README.md Use injectIsMutating to get the number of mutations currently fetching. This observable example shows how to track overall mutating and mutating for specific query keys. ```typescript import { injectIsMutating } from '@ngneat/query'; class TodoComponent { #isMutating = injectIsMutating(); // How many queries overall are currently fetching data? public isFetching$ = this.#isMutating().result$; // How many queries matching the todos prefix are currently fetching? public isFetchingTodos$ = this.#isMutating({ queryKey: ['todos'] }).result$; } ``` -------------------------------- ### Example Commit Message: Fix Release Dependency Source: https://github.com/ngneat/query/blob/main/CONTRIBUTING.md This commit message demonstrates fixing a release dependency issue. ```git fix(release): need to depend on latest rxjs and zone.js The version in our package.json gets copied to the one we publish, and users need the latest of these. ``` -------------------------------- ### Inject Query and Fetch Data within Injection Context Source: https://github.com/ngneat/query/blob/main/README.md Use `injectQuery` to get a query instance within an injection context. The `queryFn` can then use `inject(HttpClient)` to perform data fetching. ```typescript import { injectQuery } from '@ngneat/query'; export function getTodos() { const query = injectQuery(); return query({ queryKey: ['todos'] as const, queryFn: () => { return inject(HttpClient).get( 'https://jsonplaceholder.typicode.com/todos', ); }, }); } ``` -------------------------------- ### `injectIsFetching` - Global Background-Fetching Indicator Source: https://context7.com/ngneat/query/llms.txt Use `injectIsFetching` to get a function that returns an observable of the number of active queries. Call it without arguments for a global count, or with `QueryFilters` to scope it to specific queries. Useful for displaying app-wide or query-specific loading spinners. ```typescript import { Component, inject } from '@angular/core'; import { AsyncPipe } from '@angular/common'; import { injectIsFetching } from '@ngneat/query'; @Component({ standalone: true, imports: [AsyncPipe], template: ` @if (globalFetching$ | async) { } @if (todosFetching$ | async; as count) {

{{ count }} todos queries in flight

} `, }) export class AppShellComponent { #isFetching = injectIsFetching(); globalFetching$ = this.#isFetching().result$; todosFetching$ = this.#isFetching({ queryKey: ['todos'] }).result$; // Signal variant isFetchingSignal = this.#isFetching().toSignal(); } ``` -------------------------------- ### Start with Pending Query Result RxJS Operator Source: https://github.com/ngneat/query/blob/main/README.md The startWithPendingQueryResult operator prepends a pending query result to the observable stream, mimicking the initial state of a new query. It's often used in conjunction with other operators like filterSuccess. ```typescript this.todosService.getTodos().result$.pipe( filterSuccess(), switchMap(() => someSource), startWithPendingQueryResult(), ); ``` -------------------------------- ### Mock Query Object with provideQueryConfig Source: https://github.com/ngneat/query/blob/main/README.md Demonstrates how to mock a query object using provideQueryConfig for testing purposes. The mock can be provided directly or as a factory. ```typescript import { provideQueryConfig } from '@ngneat/query'; const queryMock = { use: () => ({ result$: of({ ... }), result: computed(() => ({ ... })) }) }; provideQueryConfig({ query: queryMock, // or query: () => queryMock if you need a factory }); const query = injectQuery(); query({ ... }).result() // you can call query from your custom query mock ``` -------------------------------- ### provideQueryClientOptions Source: https://github.com/ngneat/query/blob/main/README.md Inject a default config for the underlying @tanstack/query instance by using the provideQueryClientOptions({}) function. It accepts a function factory if you need an injection context while creating the configuration. ```APIDOC ## provideQueryClientOptions ### Description Provides global query client options for the application. ### Usage ```ts import { provideQueryClientOptions } from '@ngneat/query'; bootstrapApplication(AppComponent, { providers: [ provideQueryClientOptions({ defaultOptions: { queries: { staleTime: 3000, }, }, }), ], }); ``` ### Functional Factory Example ```ts import { provideQueryClientOptions } from '@ngneat/query'; const withFunctionalFactory = () => { const notificationService = inject(NotificationService); return { queryCache: new QueryCache({ onError: (error: Error) => notificationService.notifyError(error), }), defaultOptions: { queries: { staleTime: 3000, }, }, }; }; bootstrapApplication(AppComponent, { providers: [provideQueryClientOptions(withFunctionalFactory)], }); ``` ``` -------------------------------- ### Bootstrap Application with Global QueryClient Options Source: https://context7.com/ngneat/query/llms.txt Register default options for the QueryClient at the application root. This can be done with a plain config object or a factory function for injection context. ```typescript // main.ts import { bootstrapApplication } from '@angular/platform-browser'; import { provideQueryClientOptions, QueryClientConfigFn } from '@ngneat/query'; import { QueryCache } from '@tanstack/query-core'; import { inject } from '@angular/core'; import { NotificationService } from './notification.service'; import { AppComponent } from './app.component'; // Option A: plain config objectootstrapApplication(AppComponent, { providers: [ provideQueryClientOptions({ defaultOptions: { queries: { staleTime: 5 * 60 * 1000, // 5 minutes retry: 2, }, }, }), ], }); // Option B: factory function with injection context const withErrorHandler: QueryClientConfigFn = () => { const notify = inject(NotificationService); return { queryCache: new QueryCache({ onError: (error: Error) => notify.error(error.message), }), defaultOptions: { queries: { staleTime: 3000 } }, }; }; ootstrapApplication(AppComponent, { providers: [provideQueryClientOptions(withErrorHandler)], }); ``` -------------------------------- ### Query Client Injection Source: https://github.com/ngneat/query/blob/main/README.md Demonstrates how to inject the QueryClient instance using injectQueryClient() or provide it manually. ```APIDOC ## Injecting QueryClient ### Description Inject the `QueryClient` instance through the `injectQueryClient()` function or provide it manually. ### Usage **Using `injectQueryClient()`:** ```ts import { injectQueryClient } from '@ngneat/query'; @Injectable({ providedIn: 'root' }) export class TodosService { #queryClient = injectQueryClient(); } ``` **Providing `QueryClient` manually:** ```ts import { provideQueryClient } from '@ngneat/query'; import { QueryClient } from '@tanstack/query-core'; provideQueryClient(() => new QueryClient()); ``` > Functions should run inside an injection context. ``` -------------------------------- ### Provide Query Client Options with Functional Factory Source: https://github.com/ngneat/query/blob/main/README.md Use a functional factory with provideQueryClientOptions when injection context is needed to create the configuration. This allows dynamic configuration based on injected services. ```typescript import { provideQueryClientOptions } from '@ngneat/query'; const withFunctionalFactory: QueryClientConfigFn = () => { const notificationService = inject(NotificationService); return { queryCache: new QueryCache({ onError: (error: Error) => notificationService.notifyError(error), }), defaultOptions: { queries: { staleTime: 3000, }, }, }; }; bootstrapApplication(AppComponent, { providers: [provideQueryClientOptions(withFunctionalFactory)], }); ``` -------------------------------- ### Lazy Load Devtools in Production Source: https://github.com/ngneat/query/blob/main/devtools/README.md This recipe demonstrates how to lazy-load devtools in a production environment. It defines a global `window.toggleDevtools()` function that, when called, dynamically imports and mounts the devtools. ```typescript import { onlineManager } from '@tanstack/query-core'; import { APP_INITIALIZER } from '@angular/core'; import { injectQueryClient } from '@ngneat/query'; export const appConfig: ApplicationConfig = { providers: [ // ...other providers... environment.production ? { provide: APP_INITIALIZER, multi: true, useFactory: provideLazyQueryDevTools, }, : provideQueryDevTools(options), ], }; function provideLazyQueryDevTools() { const client = injectQueryClient(); return () => { // Define our global `toggleDevtools()` function to lazy-load query devtools window.toggleDevtools = () => { import('@tanstack/query-devtools').then((d) => { new d.TanstackQueryDevtools({ client, queryFlavor: '@ngneat/query', version: '5', position: 'bottom', initialIsOpen: true, buttonPosition: 'bottom-right', onlineManager, }).mount(document.body); }); }; }; } ``` -------------------------------- ### Provide Default Query Client Options Source: https://github.com/ngneat/query/blob/main/README.md Inject default configuration for the underlying @tanstack/query instance using provideQueryClientOptions. This is useful for setting global query defaults. ```typescript import { provideQueryClientOptions } from '@ngneat/query'; bootstrapApplication(AppComponent, { providers: [ provideQueryClientOptions({ defaultOptions: { queries: { staleTime: 3000, }, }, }), ], }); ``` -------------------------------- ### provideQueryClient Source: https://context7.com/ngneat/query/llms.txt Overrides the default auto-created QueryClient with a manually constructed one. This is useful for Server-Side Rendering (SSR) and testing scenarios. ```APIDOC ## provideQueryClient — Provide a custom QueryClient instance Overrides the default auto-created `QueryClient` with a manually constructed one. Useful for SSR and testing. ```ts import { provideQueryClient } from '@ngneat/query'; import { QueryClient } from '@tanstack/query-core'; bootstrapApplication(AppComponent, { providers: [ provideQueryClient( () => new QueryClient({ defaultOptions: { queries: { retry: false } } }), ), ], }); ``` ``` -------------------------------- ### provideQueryClientOptions Source: https://context7.com/ngneat/query/llms.txt Registers default options for the underlying TanStack QueryClient at the application root. It accepts a plain config object or a factory function for cases requiring Angular's injection context. ```APIDOC ## provideQueryClientOptions — Bootstrap-level global QueryClient configuration Registers default options for the underlying TanStack `QueryClient` at the application root. Accepts a plain config object or a factory function (for cases that require Angular's injection context during construction, e.g. attaching a global error handler via `QueryCache`). ```ts // main.ts import { bootstrapApplication } from '@angular/platform-browser'; import { provideQueryClientOptions, QueryClientConfigFn } from '@ngneat/query'; import { QueryCache } from '@tanstack/query-core'; import { inject } from '@angular/core'; import { NotificationService } from './notification.service'; import { AppComponent } from './app.component'; // Option A: plain config object bootstrapApplication(AppComponent, { providers: [ provideQueryClientOptions({ defaultOptions: { queries: { staleTime: 5 * 60 * 1000, // 5 minutes retry: 2, }, }, }), ], }); // Option B: factory function with injection context const withErrorHandler: QueryClientConfigFn = () => { const notify = inject(NotificationService); return { queryCache: new QueryCache({ onError: (error: Error) => notify.error(error.message), }), defaultOptions: { queries: { staleTime: 3000 } }, }; }; bootstrapApplication(AppComponent, { providers: [provideQueryClientOptions(withErrorHandler)], }); ``` ``` -------------------------------- ### Provide Custom Query Configuration Source: https://github.com/ngneat/query/blob/main/README.md Use provideQueryConfig to override default query, mutation, isMutating, isFetching, or infiniteQuery objects. Configuration can be a raw object or a factory function. ```typescript export function provideQueryConfig(config: { query?: QueryObject | (() => QueryObject); mutation?: MutationObject | (() => MutationObject); isMutating?: IsMutatingObject | (() => IsMutatingObject); isFetching?: IsFetchingObject | (() => IsFetchingObject); infiniteQuery?: InfiniteQueryObject | (() => InfiniteQueryObject); }): Provider; ``` -------------------------------- ### Displaying Mutation Status (Signal) Source: https://github.com/ngneat/query/blob/main/src/app/mutation-page/mutation-page.component.html Shows how to display the active state of a mutation using a signal. This is useful for providing real-time feedback to the user. ```html #### Is Mutating: {{ addTodoMutationsActive() }} ``` -------------------------------- ### Test Utilities: createSuccessObserverResult and createPendingObserverResult Source: https://context7.com/ngneat/query/llms.txt Factory functions for creating typed `QueryObserverResult` objects for unit testing. Use these to simulate success or pending states without making actual queries. ```typescript import { of } from 'rxjs'; import { createSuccessObserverResult, createPendingObserverResult } from '@ngneat/query'; // In a TestBed spec const successResult = createSuccessObserverResult([ { id: 1, title: 'Buy milk', completed: false }, ]); // { data: [...], isSuccess: true, isLoading: false, ... } const pendingResult = createPendingObserverResult(); // { isPending: true, isLoading: true, isFetching: true, ... } const mockQuery = { use: () => ({ result$: of(successResult), result: signal(successResult) }) }; ``` -------------------------------- ### Inject QueryClient After Manual Provision Source: https://github.com/ngneat/query/blob/main/README.md After manually providing the QueryClient, you can inject it using injectQueryClient() in your services. Ensure this is done within an injection context. ```typescript import { injectQueryClient } from '@ngneat/query'; ... #queryClient = injectQueryClient(); ``` -------------------------------- ### Handling Mutation Results (Observable API) Source: https://github.com/ngneat/query/blob/main/src/app/mutation-page/mutation-page.component.html Illustrates how to display the result of a mutation when using the Observable API with RxJS. This is useful for applications already heavily invested in RxJS patterns. ```html } @if (addTodo.result$ | async; as addTodo) { Add Todo Show Error Reset Mutation #### Mutation Observable Output ` {{ addTodo | json }} ` } ``` -------------------------------- ### injectInfiniteQuery — Paginated / load-more queries Source: https://context7.com/ngneat/query/llms.txt Wraps TanStack's `InfiniteQueryObserver`. Returns the same `result$` / `result` dual-reactive object, where `data` is `InfiniteData` containing a `pages` array. ```APIDOC ## `injectInfiniteQuery` — Paginated / load-more queries Wraps TanStack's `InfiniteQueryObserver`. Returns the same `result$` / `result` dual-reactive object, where `data` is `InfiniteData` containing a `pages` array. ### Usage Example ```typescript import { Injectable, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { injectInfiniteQuery } from '@ngneat/query'; interface Page { data: Post[]; nextId: number | null; previousId: number | null; } @Injectable({ providedIn: 'root' }) export class PostsService { #http = inject(HttpClient); #infiniteQuery = injectInfiniteQuery(); getPosts() { return this.#infiniteQuery({ queryKey: ['posts', 'infinite'] as const, queryFn: ({ pageParam }) => this.#http.get( `https://jsonplaceholder.typicode.com/posts?page=${pageParam}&limit=10`, ), initialPageParam: 0, getPreviousPageParam: (firstPage) => firstPage.previousId ?? undefined, getNextPageParam: (lastPage) => lastPage.nextId ?? undefined, }); } } // Component @Component({ standalone: true, template: ` @if (posts().isSuccess) { @for (page of posts().data.pages; track $index) { @for (post of page.data; track post.id) {

{{ post.title }}

} } @if (addTodo.result$ | async; as result) { @if (result.isLoading) {

Mutation is loading

} @if (result.isSuccess) {

Mutation was successful

} @if (result.isError) {

Mutation encountered an Error

} } `, }) export class TodosComponent { addTodo = inject(TodosService).addTodo(); onAddTodo({ title }) { this.addTodo.mutate({ title }); // Or this.addTodo.mutateAsync({ title }); } } ``` -------------------------------- ### Injecting and Using Queries Source: https://github.com/ngneat/query/blob/main/README.md Shows how to use the injectQuery function to manage queries within Angular services, supporting both Promises and Observables. ```APIDOC ## Using Queries with `injectQuery` ### Description Use the `injectQuery` function to manage queries. This function is similar to the official TanStack Query function and supports Promises and Observables for `queryFn`. ### Usage in Service ```ts import { injectQuery } from '@ngneat/query'; import { inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; @Injectable({ providedIn: 'root' }) export class TodosService { #http = inject(HttpClient); #query = injectQuery(); getTodos() { return this.#query({ queryKey: ['todos'] as const, queryFn: () => { return this.#http.get( 'https://jsonplaceholder.typicode.com/todos', ); }, }); } } ``` > The function should run inside an injection context. ### Component Usage - Observable To get an observable, use the `result$` property: ```ts @Component({ standalone: true, template: ` @if (todos.result$ | async; as result) { @if (result.isLoading) {

Loading

} @if (result.isSuccess) {

{{ result.data[0].title }}

} @if (result.isError) {

Error

} } `, }) export class TodosPageComponent { todos = inject(TodosService).getTodos(); } ``` ### Component Usage - Signal To get a signal, use the `result` property: ```ts @Component({ standalone: true, template: ` @if (todos().isLoading) { Loading } @if (todos().data; as data) {

{{ data[0].title }}

} @if (todos().isError) {

Error

} `, }) export class TodosPageComponent { todos = inject(TodosService).getTodos().result; } ``` ``` -------------------------------- ### provideQueryConfig Source: https://context7.com/ngneat/query/llms.txt A configuration function used to override the default query primitives within an Angular testing environment. It allows replacing core injection tokens like `injectQuery` and `injectMutation` with custom implementations, primarily for testing scenarios. ```APIDOC ## provideQueryConfig — Override query primitives (e.g. for testing) Replaces the internal implementations of `injectQuery`, `injectMutation`, `injectIsMutating`, `injectIsFetching`, and/or `injectInfiniteQuery` with custom objects. Accepts plain objects or factory functions. ```ts import { TestBed } from '@angular/core/testing'; import { provideQueryConfig, createSuccessObserverResult } from '@ngneat/query'; import { of } from 'rxjs'; import { signal } from '@angular/core'; const todos = [{ id: 1, title: 'Test todo', completed: false }]; const queryMock = { use: () => ({ result$: of(createSuccessObserverResult(todos)), result: signal(createSuccessObserverResult(todos)), updateOptions: () => {}, }), }; TestBed.configureTestingModule({ providers: [ provideQueryConfig({ query: queryMock }), ], }); // Any component/service calling injectQuery() now uses queryMock ``` ``` -------------------------------- ### Provide QueryClient Manually Source: https://github.com/ngneat/query/blob/main/README.md Manually provide a QueryClient instance in your Angular application's configuration. This allows for custom configurations of the QueryClient. ```typescript import { provideQueryClient } from '@ngneat/query'; import { QueryClient } from '@tanstack/query-core'; provideQueryClient(() => new QueryClient()); ``` -------------------------------- ### Displaying Prefetched Todos in Component Source: https://github.com/ngneat/query/blob/main/src/app/prefetch-page/prefetch-page.component.html This snippet shows how to iterate over prefetched todo items and display their titles. It's designed to be used within a component that receives prefetched data. ```html @for (t of todos.slice(0, 10); track t.id) { {{ t.title }} } ``` -------------------------------- ### Component Using Mutation (Signals) Source: https://github.com/ngneat/query/blob/main/README.md A component demonstrating the signal-based approach to using mutations, accessing results via the `result` getter. ```typescript @Component({ template: ` @if (addTodo.result(); as result) { @if (result.isLoading) {

Mutation is loading

} @if (result.isSuccess) {

Mutation was successful

} @if (result.isError) {

Mutation encountered an Error

} } `, }) export class TodosComponent { addTodo = inject(TodosService).addTodo(); onAddTodo({ title }) { this.addTodo.mutate({ title }); } } ``` -------------------------------- ### injectIsFetching Source: https://context7.com/ngneat/query/llms.txt Provides a global indicator for background data fetching. It returns a function that, when called with optional filters, yields an object with a reactive `result$` observable and a `toSignal()` helper to track the number of active queries. ```APIDOC ## `injectIsFetching` — Global background-fetching indicator Returns a function that, when called with optional `QueryFilters`, produces an object with `result$: Observable` and a `toSignal()` helper. Useful for app-level loading spinners. ```ts import { Component, inject } from '@angular/core'; import { AsyncPipe } from '@angular/common'; import { injectIsFetching } from '@ngneat/query'; @Component({ standalone: true, imports: [AsyncPipe], template: '\n' + '@if (globalFetching$ | async) {\n' + ' \n' + '}\n' + '\n' + '\n' + '@if (todosFetching$ | async; as count) {\n' + '

{{ count }} todos queries in flight

\n' + '}' }) export class AppShellComponent { #isFetching = injectIsFetching(); globalFetching$ = this.#isFetching().result$; todosFetching$ = this.#isFetching({ queryKey: ['todos'] }).result$; // Signal variant isFetchingSignal = this.#isFetching().toSignal(); } ``` ``` -------------------------------- ### Typed, Reusable Query Options with queryOptions Source: https://context7.com/ngneat/query/llms.txt Use `queryOptions` to extract query configurations into a factory for sharing between `injectQuery` and imperative client methods. This preserves full TypeScript inference. ```typescript import { Injectable, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { queryOptions, injectQuery, injectQueryClient } from '@ngneat/query'; interface Post { id: number; title: string; body: string; } @Injectable({ providedIn: 'root' }) export class PostsService { #http = inject(HttpClient); #query = injectQuery(); #client = injectQueryClient(); postOptions = (id: number) => queryOptions({ queryKey: ['posts', id] as const, queryFn: () => this.#http.get(`https://jsonplaceholder.typicode.com/posts/${id}`), staleTime: 5 * 1000, }); getPost(id: number) { return this.#query(this.postOptions(id)); } // Return type is inferred as Post | undefined getCachedPost(id: number) { return this.#client.getQueryData(this.postOptions(id).queryKey); } } ``` -------------------------------- ### Displaying Paginated Projects in Angular Source: https://github.com/ngneat/query/blob/main/src/app/pagination-page/pagination-page.component.html This snippet displays a list of projects fetched from an observable. It uses Angular's async pipe and for loop to iterate over the projects, showing their ID and name. It also includes conditional rendering for loading states and navigation buttons. ```html @if (projects$ | async; as projects) { @if (projects.isSuccess) { @for (project of projects.data.projects; track project.id) { } {{ project.id }} {{ project.name }} } Previous @if (projects.isFetching) { } Next page } ``` -------------------------------- ### SSR: Server-Side Data Dehydration Source: https://github.com/ngneat/query/blob/main/README.md On the server, initialize `QueryClient`, render the application, dehydrate the query state, and inject it into the HTML as a script tag. Clear the client after sending the response. ```typescript import { provideQueryClient } from '@ngneat/query'; import { QueryClient, dehydrate } from '@tanstack/query-core'; import { renderApplication } from '@angular/platform-server'; async function handleRequest(req, res) { const queryClient = new QueryClient(); let html = await renderApplication(AppComponent, { providers: [provideQueryClient(queryClient)], }); const queryState = JSON.stringify(dehydrate(queryClient)); html = html.replace( '', ``, ); res.send(html); queryClient.clear(); } ``` -------------------------------- ### Inject QueryClient Instance Source: https://github.com/ngneat/query/blob/main/README.md Inject the QueryClient instance using injectQueryClient() within an Angular service. This is the recommended way to access the client. ```typescript import { injectQueryClient } from '@ngneat/query'; @Injectable({ providedIn: 'root' }) export class TodosService { #queryClient = injectQueryClient(); } ``` -------------------------------- ### updateOptions Source: https://context7.com/ngneat/query/llms.txt A method available on query observers that allows for reactive updating of query parameters in-place. This is useful for scenarios where query inputs change, such as route parameters, without needing to tear down and recreate the query observer. ```APIDOC ## updateOptions — Reactively change query parameters in-place Replaces the active query options on an existing observer result without tearing down and re-creating it. Useful for input-driven queries (e.g. route params triggering different IDs). ```ts import { Component, inject, Input } from '@angular/core'; @Component({ standalone: true, template: ` @if (user().data; as u) {

{{ u.email }}

} `, }) export class UserDetailComponent { #usersService = inject(UsersService); userQuery = this.#usersService.getUser(1); user = this.userQuery.result; @Input() set userId(id: string) { // Updates the live observer — no unnecessary component re-creation this.userQuery.updateOptions(this.#usersService.getUserOptions(+id)); } } ``` ``` -------------------------------- ### Display Signal Result for Todos Source: https://github.com/ngneat/query/blob/main/src/app/basic-page/todos-page.component.html Use this snippet to display the fetched todo data when using signals. It handles loading and error states. ```html @if (todos().isLoading) { } @if (todos().data; as data) { {{ data[0].title }} } @if (todos().isError) { Error } ``` -------------------------------- ### Access and Use the QueryClient Instance Source: https://context7.com/ngneat/query/llms.txt Retrieve the singleton QueryClient instance using Angular's DI. The injected client extends the upstream QueryClient to accept Observables in addition to Promises for queryFn. ```typescript import { Injectable, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { injectQueryClient, queryOptions } from '@ngneat/query'; interface Todo { id: number; title: string; completed: boolean; } @Injectable({ providedIn: 'root' }) export class TodosService { #http = inject(HttpClient); #client = injectQueryClient(); todosOptions = queryOptions({ queryKey: ['todos'] as const, queryFn: () => this.#http.get('https://jsonplaceholder.typicode.com/todos'), staleTime: 60_000, }); // Imperatively prefetch on hover / route guard async prefetch() { await this.#client.prefetchQuery(this.todosOptions); } // Read typed cached data getCached(): Todo[] | undefined { return this.#client.getQueryData(this.todosOptions.queryKey); // ^? Todo[] | undefined — inferred from queryOptions } // Invalidate after a mutation async invalidate() { await this.#client.invalidateQueries({ queryKey: ['todos'] }); } } ``` -------------------------------- ### SSR: Client-Side Data Hydration Source: https://github.com/ngneat/query/blob/main/README.md On the client, parse the dehydrated state from `window.__QUERY_STATE__`, hydrate the `QueryClient`, and bootstrap the application with the hydrated client and necessary Angular modules for server transition. ```typescript import { importProvidersFrom } from '@angular/core'; import { bootstrapApplication, BrowserModule } from '@angular/platform-browser'; import { provideQueryClient } from '@ngneat/query'; import { QueryClient, hydrate } from '@tanstack/query-core'; const queryClient = new QueryClient(); const dehydratedState = JSON.parse(window.__QUERY_STATE__); hydrate(queryClient, dehydratedState); bootstrapApplication(AppComponent, { providers: [ importProvidersFrom( BrowserModule.withServerTransition({ appId: 'server-app' }), ), provideQueryClient(queryClient), ], }); ``` -------------------------------- ### queryOptions — Typed, reusable query option factories Source: https://context7.com/ngneat/query/llms.txt Extracts query options into a standalone factory so they can be shared between `injectQuery` and imperative client methods while preserving full TypeScript inference (including `getQueryData` return types). ```APIDOC ## `queryOptions` — Typed, reusable query option factories Extracts query options into a standalone factory so they can be shared between `injectQuery` and imperative client methods while preserving full TypeScript inference (including `getQueryData` return types). ### Usage Example ```typescript import { Injectable, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { queryOptions, injectQuery, injectQueryClient } from '@ngneat/query'; interface Post { id: number; title: string; body: string; } @Injectable({ providedIn: 'root' }) export class PostsService { #http = inject(HttpClient); #query = injectQuery(); #client = injectQueryClient(); postOptions = (id: number) => queryOptions({ queryKey: ['posts', id] as const, queryFn: () => this.#http.get(`https://jsonplaceholder.typicode.com/posts/${id}`), staleTime: 5 * 1000, }); getPost(id: number) { return this.#query(this.postOptions(id)); } // Return type is inferred as Post | undefined getCachedPost(id: number) { return this.#client.getQueryData(this.postOptions(id).queryKey); } } ``` ``` -------------------------------- ### Reactively Update Query Options with updateOptions Source: https://context7.com/ngneat/query/llms.txt The `updateOptions` method allows you to reactively change query parameters on an existing observer without tearing it down. This is useful for input-driven queries, such as those triggered by route parameters. ```typescript import { Component, inject, Input } from '@angular/core'; @Component({ standalone: true, template: ` @if (user().data; as u) {

{{ u.email }}

} `, }) export class UserDetailComponent { #usersService = inject(UsersService); userQuery = this.#usersService.getUser(1); user = this.userQuery.result; @Input() set userId(id: string) { // Updates the live observer — no unnecessary component re-creation this.userQuery.updateOptions(this.#usersService.getUserOptions(+id)); } } ``` -------------------------------- ### Override Query Primitives with provideQueryConfig Source: https://context7.com/ngneat/query/llms.txt Use `provideQueryConfig` to replace internal query primitives like `injectQuery` with custom implementations, primarily for testing purposes. It accepts plain objects or factory functions. ```typescript import { TestBed } from '@angular/core/testing'; import { provideQueryConfig, createSuccessObserverResult } from '@ngneat/query'; import { of } from 'rxjs'; import { signal } from '@angular/core'; const todos = [{ id: 1, title: 'Test todo', completed: false }]; const queryMock = { use: () => ({ result$: of(createSuccessObserverResult(todos)), result: signal(createSuccessObserverResult(todos)), updateOptions: () => {}, }), }; TestBed.configureTestingModule({ providers: [ provideQueryConfig({ query: queryMock }), ], }); // Any component/service calling injectQuery() now uses queryMock ``` -------------------------------- ### RxJS Operators Source: https://github.com/ngneat/query/blob/main/README.md A collection of RxJS operators to enhance query result handling. ```APIDOC ## RxJS Operators ### filterSuccessResult #### Description Filters the observable stream to only emit successful query results. #### Usage `todosService.getTodos().result$.pipe(filterSuccessResult())` ### filterErrorResult #### Description Filters the observable stream to only emit error query results. #### Usage `todosService.getTodos().result$.pipe(filterErrorResult())` ### tapSuccessResult #### Description Runs a side effect only when the query result is successful. #### Usage `todosService.getTodos().result$.pipe(tapSuccessResult(console.log))` ### tapErrorResult #### Description Runs a side effect only when the query result is an error. #### Usage `todosService.getTodos().result$.pipe(tapErrorResult(console.log))` ### mapResultData #### Description Maps the `data` property of the query result object in case of a successful result. #### Usage ```ts this.todosService.getTodos().result$.pipe( mapResultData((data) => { return { todos: data.todos.filter(predicate), }; }), ); ``` ### takeUntilResultFinalize #### Description Takes values emitted by the source observable until the `isFetching` property on the result is false. Intended for use when an observable stream should be listened to until the result has finished fetching (success or error). #### Usage `todosService.getTodos().result$.pipe(takeUntilResultFinalize())` ### takeUntilResultSuccess #### Description Takes values emitted by the source observable until the `isSuccess` property on the result is true. Intended for use when an observable stream should be listened to until a successful result is emitted. #### Usage `todosService.getTodos().result$.pipe(takeUntilResultSuccess())` ### takeUntilResultError #### Description Takes values emitted by the source observable until the `isError` property on the result is true. Intended for use when an observable stream should be listened to until an error result is emitted. #### Usage `todosService.getTodos().result$.pipe(takeUntilResultError())` ### startWithPendingQueryResult #### Description Starts the observable stream with a pending query result, similar to what would be returned upon creating a normal query. #### Usage ```ts this.todosService.getTodos().result$.pipe( filterSuccess(), switchMap(() => someSource), startWithPendingQueryResult(), ); ``` ``` -------------------------------- ### Inject Infinite Query Source: https://github.com/ngneat/query/blob/main/README.md Utilize the `injectInfiniteQuery` function for implementing infinite queries, similar to the official TanStack Query implementation. ```typescript import { injectInfiniteQuery } from '@ngneat/query'; @Injectable({ providedIn: 'root' }) export class PostsService { #query = injectInfiniteQuery(); getPosts() { return this.#query({ queryKey: ['posts'], queryFn: ({ pageParam }) => getProjects(pageParam), initialPageParam: 0, getPreviousPageParam: (firstPage) => firstPage.previousId, getNextPageParam: (lastPage) => lastPage.nextId, }); } } ``` -------------------------------- ### Display Observable Result for Todos Source: https://github.com/ngneat/query/blob/main/src/app/basic-page/todos-page.component.html Use this snippet to display the fetched todo data when using observables. It handles loading and error states using the async pipe. ```html @if (todosResult.result$ | async; as result) { @if (result.isLoading) { } @if (result.isSuccess) { {{ result.data[0].title }} } @if (result.isError) { Error } } ``` -------------------------------- ### Define Reusable Query Options Source: https://github.com/ngneat/query/blob/main/README.md Use the `queryOptions` helper to extract query options into a function, enabling type inference when sharing them between `query` and `prefetchQuery`. ```typescript import { queryOptions } from '@ngneat/query'; function groupOptions() { return queryOptions({ queryKey: ['groups'] as const, queryFn: fetchGroups, staleTime: 5 * 1000, }); } ```