Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Because TanStack Query's fetching mechanisms are agnostically built on Promises,
- Mock responses in unit tests using [provideHttpClientTesting](https://angular.dev/guide/http/testing).
- [Interceptors](https://angular.dev/guide/http/interceptors) can be used for a wide range of functionality including adding authentication headers, performing logging, etc. While some data fetching libraries have their own interceptor system, `HttpClient` interceptors are integrated with Angular's dependency injection system.
- `HttpClient` automatically informs [`PendingTasks`](https://angular.dev/api/core/PendingTasks#), which enables Angular to be aware of pending requests. Unit tests and SSR can use the resulting application _stableness_ information to wait for pending requests to finish. This makes unit testing much easier for [Zoneless](https://angular.dev/guide/zoneless) applications.
- When using SSR, `HttpClient` will [cache requests](https://angular.dev/guide/ssr#caching-data-when-using-HttpClient) performed on the server. This will prevent unneeded requests on the client. `HttpClient` SSR caching works out of the box. TanStack Query has its own hydration functionality which may be more powerful but requires some setup. Which one fits your needs best depends on your use case.
- When using SSR, `HttpClient` will [cache requests](https://angular.dev/guide/ssr#caching-data-when-using-HttpClient) performed on the server. This will prevent unneeded requests on the client. TanStack Query also supports built-in SSR dehydration and hydration through `provideTanStackQuery`, `TransferState`, and features such as `withHydrationKey` and `withNoQueryHydration`. Which one fits your needs best depends on your use case.

### Using observables in `queryFn`

Expand Down
74 changes: 39 additions & 35 deletions docs/framework/angular/devtools.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,23 @@ title: Devtools

## Enable devtools

Add the devtools package (in addition to `@tanstack/angular-query-experimental`):

```bash
npm install @tanstack/angular-query-devtools
```

The devtools help you debug and inspect your queries and mutations. You can enable the devtools by adding `withDevtools` to `provideTanStackQuery`.

By default, Angular Query Devtools are only included in development mode bundles, so you don't need to worry about excluding them during a production build.
By default, Angular Query Devtools only load in development.

```ts
import {
QueryClient,
provideTanStackQuery,
} from '@tanstack/angular-query-experimental'

import { withDevtools } from '@tanstack/angular-query-experimental/devtools'
import { withDevtools } from '@tanstack/angular-query-devtools'

export const appConfig: ApplicationConfig = {
providers: [provideTanStackQuery(new QueryClient(), withDevtools())],
Expand All @@ -30,28 +36,28 @@ export const appConfig: ApplicationConfig = {

## Devtools in production

Devtools are automatically excluded from production builds. However, it might be desirable to lazy load the devtools in production.

To use `withDevtools` in production builds, import using the `production` sub-path. The function exported from the production subpath is identical to the main one, but won't be excluded from production builds.
If you need the real implementation in production, import from the `production` entrypoint.

```ts
import { withDevtools } from '@tanstack/angular-query-experimental/devtools/production'
import { withDevtools } from '@tanstack/angular-query-devtools/production'
```

To control when devtools are loaded, you can use the `loadDevtools` option.
To control when devtools are loaded, use the `loadDevtools` option.

When not setting the option or setting it to 'auto', the devtools will be loaded automatically only when Angular runs in development mode.
When omitted or set to `'auto'`, devtools only load in development mode.

```ts
import { withDevtools } from '@tanstack/angular-query-experimental/devtools'
import { withDevtools } from '@tanstack/angular-query-devtools'

provideTanStackQuery(new QueryClient(), withDevtools())
providers: [provideTanStackQuery(new QueryClient(), withDevtools())]

// which is equivalent to
provideTanStackQuery(
new QueryClient(),
withDevtools(() => ({ loadDevtools: 'auto' })),
)
providers: [
provideTanStackQuery(
new QueryClient(),
withDevtools(() => ({ loadDevtools: 'auto' })),
),
]
```

When setting the option to true, the devtools will be loaded in both development and production mode.
Expand All @@ -61,29 +67,30 @@ This is useful if you want to load devtools based on [Angular environment config
```ts
import { environment } from './environments/environment'
// Make sure to use the production sub-path to load devtools in production builds
import { withDevtools } from '@tanstack/angular-query-experimental/devtools/production'

provideTanStackQuery(
new QueryClient(),
withDevtools(() => ({ loadDevtools: environment.loadDevtools })),
)
import { withDevtools } from '@tanstack/angular-query-devtools/production'

providers: [
provideTanStackQuery(
new QueryClient(),
withDevtools(() => ({ loadDevtools: environment.loadDevtools })),
),
]
```

When setting the option to false, the devtools will not be loaded.

```ts
provideTanStackQuery(
new QueryClient(),
withDevtools(() => ({ loadDevtools: false })),
)
providers: [
provideTanStackQuery(
new QueryClient(),
withDevtools(() => ({ loadDevtools: false })),
),
]
```

## Derive options through reactivity

Options are passed to `withDevtools` from a callback function to support reactivity through signals. In the following example
a signal is created from a RxJS observable that emits on a keyboard shortcut. When the derived signal is set to true, the devtools are lazily loaded.

The example below always loads devtools in development mode and loads on-demand in production mode when a keyboard shortcut is pressed.
Options can be returned from a callback so they can react to signals. For example, a signal derived from a keyboard shortcut can enable devtools on demand:

```ts
import { Injectable, isDevMode } from '@angular/core'
Expand All @@ -107,14 +114,12 @@ export class DevtoolsOptionsManager {
}
```

If you want to use an injectable such as a service in the callback you can use `deps`. The injected value will be passed as parameter to the callback function.

This is similar to `deps` in Angular's [`useFactory`](https://angular.dev/guide/di/dependency-injection-providers#factory-providers-usefactory) provider.
To use an injectable such as a service in the callback, pass it through `deps`:

```ts
// ...
// πŸ‘‡ Note we import from the production sub-path to enable devtools lazy loading in production builds
import { withDevtools } from '@tanstack/angular-query-experimental/devtools/production'
import { withDevtools } from '@tanstack/angular-query-devtools/production'

export const appConfig: ApplicationConfig = {
providers: [
Expand All @@ -126,7 +131,6 @@ export const appConfig: ApplicationConfig = {
loadDevtools: devToolsOptionsManager.loadDevtools(),
}),
{
// `deps` is used to inject and pass `DevtoolsOptionsManager` to the `withDevtools` callback.
deps: [DevtoolsOptionsManager],
},
),
Expand All @@ -140,8 +144,8 @@ export const appConfig: ApplicationConfig = {
Of these options `loadDevtools`, `client`, `position`, `errorTypes`, `buttonPosition`, and `initialIsOpen` support reactivity through signals.

- `loadDevtools?: 'auto' | boolean`
- Defaults to `auto`: lazily loads devtools when in development mode. Skips loading in production mode.
- Use this to control if the devtools are loaded.
- Omit or `'auto'`: load devtools only in development mode.
- Use this to control whether devtools load when using the `/production` import.
- `initialIsOpen?: Boolean`
- Set this to `true` if you want the tools to default to being open
- `buttonPosition?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "relative"`
Expand Down
8 changes: 7 additions & 1 deletion docs/framework/angular/guides/dependent-queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@ projectsQuery = injectQuery(() => ({
[//]: # 'Example2'

```ts
// injectQueries is under development for Angular Query
projectsQueries = injectQueries(() => ({
queries:
this.userQuery.data()?.projectIds.map((projectId) => ({
queryKey: ['project', projectId],
queryFn: () => getProjectById(projectId),
})) ?? [],
}))
```

[//]: # 'Example2'
9 changes: 7 additions & 2 deletions docs/framework/angular/guides/parallel-queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ replace:
[//]: # 'Example'

```ts
@Component({
// ...
})
export class AppComponent {
// The following queries will execute in parallel
usersQuery = injectQuery(() => ({ queryKey: ['users'], queryFn: fetchUsers }))
Expand All @@ -39,12 +42,14 @@ TanStack Query provides `injectQueries`, which you can use to dynamically execut
[//]: # 'Example2'

```ts
@Component({
// ...
})
export class AppComponent {
users = signal<Array<User>>([])

// Please note injectQueries is under development and this code does not work yet
userQueries = injectQueries(() => ({
queries: users().map((user) => {
queries: this.users().map((user) => {
return {
queryKey: ['user', user.id],
queryFn: () => fetchUserById(user.id),
Expand Down
100 changes: 100 additions & 0 deletions docs/framework/angular/guides/ssr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
---
id: ssr
title: SSR
---

For [Angular SSR](https://angular.dev/guide/ssr), you can run queries on the server, embed the serialized cache in the HTML response, and hydrate the same data in the browser so the client does not refetch immediately.

[`provideTanStackQuery`](../reference/functions/provideTanStackQuery.md) serializes the `QueryClient` cache during SSR and restores it when the browser app boots. This uses Angular's `TransferState` internally.

An end-to-end sample lives at `examples/angular/ssr`. The `examples/angular/ssr-persist` example builds on the same setup with browser persistence.

## Query client token

For SSR, define an `InjectionToken` with a factory and provide that token to `provideTanStackQuery`. This keeps the docs and examples aligned with Angular's DI model and avoids helper-function-based query-client setup.

```ts
import { InjectionToken } from '@angular/core'
import { QueryClient } from '@tanstack/angular-query-experimental'

export const SHARED_QUERY_DEFAULTS = {
staleTime: 1000 * 30,
gcTime: 1000 * 60 * 60 * 24,
} as const

export const QUERY_CLIENT = new InjectionToken<QueryClient>('QUERY_CLIENT', {
factory: () =>
new QueryClient({
defaultOptions: {
queries: {
...SHARED_QUERY_DEFAULTS,
},
},
}),
})
```

## Browser config

Use the token with `provideTanStackQuery` in your application config. If you want devtools, import them from the standalone devtools package.

```ts
import type { ApplicationConfig } from '@angular/core'
import { provideHttpClient } from '@angular/common/http'
import {
provideClientHydration,
withEventReplay,
} from '@angular/platform-browser'
import { provideTanStackQuery } from '@tanstack/angular-query-experimental'
import { withDevtools } from '@tanstack/angular-query-devtools'
import { QUERY_CLIENT } from './query-client'

export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(),
provideClientHydration(withEventReplay()),
provideTanStackQuery(QUERY_CLIENT, withDevtools()),
],
}
```

## Server config

Each SSR request should bootstrap a fresh application, and Angular will resolve the token factory in that request-scoped injector. Merge your browser config with `provideServerRendering` in the server config.

```ts
import { mergeApplicationConfig } from '@angular/core'
import { provideServerRendering, withRoutes } from '@angular/ssr'
import { appConfig } from './app.config'
import { serverRoutes } from './app.routes.server'

export const serverConfig = mergeApplicationConfig(appConfig, {
providers: [provideServerRendering(withRoutes(serverRoutes))],
})
```

## Multiple query clients

Built-in hydration uses a default transfer key. For a second `QueryClient` in a child injector, pass a distinct key with `withHydrationKey` so each client's serialized cache stays separate.

```ts
providers: [
provideTanStackQuery(
SECONDARY_QUERY_CLIENT,
withHydrationKey('my-secondary-query-cache'),
),
]
```

## Disabling built-in hydration

If you need to opt out of TanStack Query's built-in `TransferState` integration for a specific injector, add `withNoQueryHydration()`.

```ts
providers: [provideTanStackQuery(QUERY_CLIENT, withNoQueryHydration())]
```

## See also

- [Angular HttpClient and data fetching](../angular-httpclient-and-other-data-fetching-clients.md)
- [Devtools](../devtools.md)
8 changes: 7 additions & 1 deletion docs/framework/angular/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ title: Installation

### NPM

_Angular Query is compatible with Angular v16 and higher_
_Angular Query is compatible with Angular v19 and higher_

```bash
npm i @tanstack/angular-query-experimental
Expand All @@ -31,4 +31,10 @@ or
bun add @tanstack/angular-query-experimental
```

If you want Angular Query devtools, install the standalone devtools package as well:

```bash
npm i @tanstack/angular-query-devtools
```

> Wanna give it a spin before you download? Try out the [simple](./examples/simple) or [basic](./examples/basic) examples!
9 changes: 4 additions & 5 deletions docs/framework/angular/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ We are in the process of getting to a stable API for TanStack Query on Angular.

## Supported Angular Versions

TanStack Query is compatible with Angular v16 and higher.
TanStack Query is compatible with Angular v19 and higher.

TanStack Query (FKA React Query) is often described as the missing data-fetching library for web applications, but in more technical terms, it makes **fetching, caching, synchronizing and updating server state** in your web applications a breeze.

Expand Down Expand Up @@ -73,11 +73,10 @@ import { lastValueFrom } from 'rxjs'
template: `
@if (query.isPending()) {
Loading...
}
@if (query.error()) {
} @else if (query.isError()) {
An error has occurred: {{ query.error().message }}
}
@if (query.data(); as data) {
} @else if (query.isSuccess()) {
@let data = query.data();
<h1>{{ data.name }}</h1>
<p>{{ data.description }}</p>
<strong>πŸ‘€ {{ data.subscribers_count }}</strong>
Expand Down
2 changes: 1 addition & 1 deletion docs/framework/angular/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import {
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [provideTanStackQuery(new QueryClient())],
providers: [provideTanStackQuery(new QueryClient()), provideHttpClient()],
bootstrap: [AppComponent],
})
export class AppModule {}
Expand Down
Loading
Loading