or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.mdinfinite-queries.mdmulti-query-operations.mdmutation-management.mdoptions-helpers.mdprovider-setup.mdquery-management.mdstatus-monitoring.md

query-management.mddocs/

0

# Query Management

1

2

Core query functionality for fetching and caching data with automatic refetching, stale-while-revalidate patterns, and reactive updates using Angular signals.

3

4

## Capabilities

5

6

### Inject Query

7

8

Creates a reactive query that automatically fetches data and provides signals for all query states.

9

10

```typescript { .api }

11

/**

12

* Injects a query: a declarative dependency on an asynchronous source of data that is tied to a unique key.

13

* @param injectQueryFn - A function that returns query options

14

* @param options - Additional configuration including custom injector

15

* @returns The query result with signals for reactive access

16

*/

17

function injectQuery<TQueryFnData, TError, TData, TQueryKey>(

18

injectQueryFn: () => CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>,

19

options?: InjectQueryOptions

20

): CreateQueryResult<TData, TError>;

21

22

// Overloads for different initial data scenarios

23

function injectQuery<TQueryFnData, TError, TData, TQueryKey>(

24

injectQueryFn: () => DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,

25

options?: InjectQueryOptions

26

): DefinedCreateQueryResult<TData, TError>;

27

28

function injectQuery<TQueryFnData, TError, TData, TQueryKey>(

29

injectQueryFn: () => UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,

30

options?: InjectQueryOptions

31

): CreateQueryResult<TData, TError>;

32

```

33

34

**Usage Examples:**

35

36

```typescript

37

import { injectQuery } from "@tanstack/angular-query-experimental";

38

import { Component, inject, signal } from "@angular/core";

39

import { HttpClient } from "@angular/common/http";

40

41

@Component({

42

selector: 'app-user-profile',

43

template: `

44

<div *ngIf="userQuery.isPending()">Loading user...</div>

45

<div *ngIf="userQuery.isError()">Error: {{ userQuery.error()?.message }}</div>

46

<div *ngIf="userQuery.isSuccess()">

47

<h2>{{ userQuery.data()?.name }}</h2>

48

<p>{{ userQuery.data()?.email }}</p>

49

</div>

50

`

51

})

52

export class UserProfileComponent {

53

#http = inject(HttpClient);

54

userId = signal(1);

55

56

// Basic query

57

userQuery = injectQuery(() => ({

58

queryKey: ['user', this.userId()],

59

queryFn: () => this.#http.get<User>(`/api/users/${this.userId()}`),

60

staleTime: 5 * 60 * 1000, // 5 minutes

61

refetchOnWindowFocus: true

62

}));

63

64

// Query with enabled condition

65

userDetailsQuery = injectQuery(() => ({

66

queryKey: ['userDetails', this.userId()],

67

queryFn: () => this.#http.get<UserDetails>(`/api/users/${this.userId()}/details`),

68

enabled: !!this.userId() && this.userId() > 0

69

}));

70

}

71

72

interface User {

73

id: number;

74

name: string;

75

email: string;

76

}

77

78

interface UserDetails {

79

bio: string;

80

joinDate: string;

81

lastLogin: string;

82

}

83

```

84

85

### Query Options Interface

86

87

Comprehensive options for configuring query behavior.

88

89

```typescript { .api }

90

interface CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey> {

91

/** Unique key for the query, used for caching and invalidation */

92

queryKey: TQueryKey;

93

/** Function that returns a promise resolving to the data */

94

queryFn: QueryFunction<TQueryFnData, TQueryKey>;

95

/** Whether the query should automatically execute */

96

enabled?: boolean;

97

/** Time in milliseconds after which data is considered stale */

98

staleTime?: number;

99

/** Time in milliseconds after which unused data is garbage collected */

100

gcTime?: number;

101

/** Whether to refetch when window regains focus */

102

refetchOnWindowFocus?: boolean;

103

/** Whether to refetch when component reconnects */

104

refetchOnReconnect?: boolean;

105

/** Interval in milliseconds for automatic refetching */

106

refetchInterval?: number;

107

/** Whether to continue refetching while window is hidden */

108

refetchIntervalInBackground?: boolean;

109

/** Number of retry attempts on failure */

110

retry?: boolean | number | ((failureCount: number, error: TError) => boolean);

111

/** Delay function for retry attempts */

112

retryDelay?: number | ((retryAttempt: number, error: TError) => number);

113

/** Initial data to use while loading */

114

initialData?: TData | (() => TData);

115

/** Placeholder data to show while loading */

116

placeholderData?: TData | ((previousData: TData | undefined) => TData);

117

/** Function to transform query data */

118

select?: (data: TQueryFnData) => TData;

119

/** Whether to throw errors instead of setting error state */

120

throwOnError?: boolean | ((error: TError) => boolean);

121

/** Structural sharing to optimize re-renders */

122

structuralSharing?: boolean | ((oldData: TData | undefined, newData: TData) => TData);

123

}

124

```

125

126

### Query Result Interface

127

128

Signal-based result object providing reactive access to query state.

129

130

```typescript { .api }

131

interface CreateQueryResult<TData, TError> {

132

/** Signal containing the query data */

133

data: Signal<TData | undefined>;

134

/** Signal containing any error that occurred */

135

error: Signal<TError | null>;

136

/** Signal indicating if query is currently loading (first time) */

137

isLoading: Signal<boolean>;

138

/** Signal indicating if query is pending (loading or fetching) */

139

isPending: Signal<boolean>;

140

/** Signal indicating if query completed successfully */

141

isSuccess: Signal<boolean>;

142

/** Signal indicating if query resulted in error */

143

isError: Signal<boolean>;

144

/** Signal indicating if query is currently fetching */

145

isFetching: Signal<boolean>;

146

/** Signal indicating if query is refetching in background */

147

isRefetching: Signal<boolean>;

148

/** Signal indicating if query is stale */

149

isStale: Signal<boolean>;

150

/** Signal containing query status */

151

status: Signal<'pending' | 'error' | 'success'>;

152

/** Signal containing fetch status */

153

fetchStatus: Signal<'fetching' | 'paused' | 'idle'>;

154

/** Signal containing timestamp of last successful fetch */

155

dataUpdatedAt: Signal<number>;

156

/** Signal containing timestamp of last error */

157

errorUpdatedAt: Signal<number>;

158

/** Signal containing current failure count */

159

failureCount: Signal<number>;

160

/** Signal containing failure reason */

161

failureReason: Signal<TError | null>;

162

163

// Type narrowing methods

164

isSuccess(this: CreateQueryResult<TData, TError>): this is CreateQueryResult<TData, TError>;

165

isError(this: CreateQueryResult<TData, TError>): this is CreateQueryResult<TData, TError>;

166

isPending(this: CreateQueryResult<TData, TError>): this is CreateQueryResult<TData, TError>;

167

}

168

169

interface DefinedCreateQueryResult<TData, TError> extends CreateQueryResult<TData, TError> {

170

/** Signal containing the query data (guaranteed to be defined) */

171

data: Signal<TData>;

172

}

173

```

174

175

### Options Configuration

176

177

Configuration interface for injectQuery behavior.

178

179

```typescript { .api }

180

interface InjectQueryOptions {

181

/**

182

* The Injector in which to create the query.

183

* If not provided, the current injection context will be used instead (via inject).

184

*/

185

injector?: Injector;

186

}

187

```

188

189

### Helper Types for Initial Data

190

191

Type definitions for different initial data scenarios.

192

193

```typescript { .api }

194

type UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey> =

195

CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey> & {

196

initialData?: undefined | InitialDataFunction<NonUndefinedGuard<TQueryFnData>> | NonUndefinedGuard<TQueryFnData>;

197

};

198

199

type DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey> =

200

Omit<CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>, 'queryFn'> & {

201

initialData: NonUndefinedGuard<TQueryFnData> | (() => NonUndefinedGuard<TQueryFnData>);

202

queryFn?: QueryFunction<TQueryFnData, TQueryKey>;

203

};

204

```

205

206

## Advanced Usage Patterns

207

208

### Reactive Queries with Signals

209

210

```typescript

211

@Component({})

212

export class ReactiveQueryComponent {

213

#http = inject(HttpClient);

214

215

// Signal for reactive dependency

216

searchTerm = signal('');

217

218

// Query automatically updates when searchTerm changes

219

searchQuery = injectQuery(() => ({

220

queryKey: ['search', this.searchTerm()],

221

queryFn: () => this.#http.get<SearchResult[]>(`/api/search?q=${this.searchTerm()}`),

222

enabled: this.searchTerm().length > 2, // Only search if term is long enough

223

staleTime: 30000 // 30 seconds

224

}));

225

}

226

```

227

228

### Dependent Queries

229

230

```typescript

231

@Component({})

232

export class DependentQueriesComponent {

233

#http = inject(HttpClient);

234

235

userQuery = injectQuery(() => ({

236

queryKey: ['user'],

237

queryFn: () => this.#http.get<User>('/api/user')

238

}));

239

240

// This query depends on userQuery data

241

userPostsQuery = injectQuery(() => ({

242

queryKey: ['posts', this.userQuery.data()?.id],

243

queryFn: () => this.#http.get<Post[]>(`/api/users/${this.userQuery.data()!.id}/posts`),

244

enabled: !!this.userQuery.data()?.id

245

}));

246

}

247

```

248

249

### Error Handling

250

251

```typescript

252

@Component({})

253

export class ErrorHandlingComponent {

254

#http = inject(HttpClient);

255

256

dataQuery = injectQuery(() => ({

257

queryKey: ['data'],

258

queryFn: () => this.#http.get<Data>('/api/data'),

259

retry: (failureCount, error) => {

260

// Only retry on network errors, not 4xx errors

261

return failureCount < 3 && error.status >= 500;

262

},

263

retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),

264

throwOnError: false // Handle errors in component instead of throwing

265

}));

266

267

// Access error state

268

get errorMessage() {

269

const error = this.dataQuery.error();

270

return error ? `Failed to load data: ${error.message}` : '';

271

}

272

}

273

```