or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

helpers.mdindex.mdmutations.mdplugin-setup.mdqueries.mdquery-client.mdstatus-utilities.md

helpers.mddocs/

0

# Helper Functions

1

2

Type-safe helper functions for creating query and infinite query options, providing enhanced TypeScript inference and reusable query configurations.

3

4

## Capabilities

5

6

### queryOptions

7

8

Helper function for creating type-safe query options with enhanced TypeScript inference.

9

10

```typescript { .api }

11

/**

12

* Create type-safe query options with enhanced TypeScript inference

13

* @param options - Query options with defined initial data

14

* @returns Enhanced query options with type information

15

*/

16

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

17

options: DefinedInitialQueryOptions<TQueryFnData, TError, TData, TQueryKey>

18

): DefinedInitialQueryOptions<TQueryFnData, TError, TData, TQueryKey> & {

19

queryKey: DataTag<TQueryKey, TQueryFnData, TError>;

20

};

21

22

/**

23

* Create type-safe query options with enhanced TypeScript inference

24

* @param options - Query options with undefined initial data

25

* @returns Enhanced query options with type information

26

*/

27

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

28

options: UndefinedInitialQueryOptions<TQueryFnData, TError, TData, TQueryKey>

29

): UndefinedInitialQueryOptions<TQueryFnData, TError, TData, TQueryKey> & {

30

queryKey: DataTag<TQueryKey, TQueryFnData, TError>;

31

};

32

33

// Base implementation for runtime usage

34

function queryOptions(options: unknown): unknown;

35

```

36

37

**Usage Examples:**

38

39

```typescript

40

import { queryOptions, useQuery } from '@tanstack/vue-query';

41

42

// Basic query options

43

const todosQuery = queryOptions({

44

queryKey: ['todos'],

45

queryFn: () => fetch('/api/todos').then(res => res.json()),

46

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

47

});

48

49

// Use with useQuery - full type inference

50

const { data: todos } = useQuery(todosQuery);

51

52

// Query options with parameters

53

const userQuery = (userId: number) => queryOptions({

54

queryKey: ['user', userId],

55

queryFn: ({ queryKey }) =>

56

fetch(`/api/users/${queryKey[1]}`).then(res => res.json()),

57

enabled: !!userId,

58

});

59

60

// Use with dynamic parameters

61

const userId = ref(1);

62

const { data: user } = useQuery(userQuery(userId.value));

63

64

// Query options with data transformation

65

const postsQuery = queryOptions({

66

queryKey: ['posts'],

67

queryFn: () => fetch('/api/posts').then(res => res.json()),

68

select: (posts) => posts.filter(post => post.published),

69

staleTime: 1000 * 60 * 10,

70

});

71

72

// Query options with initial data

73

const cachedUserQuery = (userId: number, initialData?: User) => queryOptions({

74

queryKey: ['user', userId],

75

queryFn: () => fetch(`/api/users/${userId}`).then(res => res.json()),

76

initialData,

77

staleTime: initialData ? 0 : 1000 * 60 * 5,

78

});

79

80

// Reusable query configurations

81

const createApiQuery = <T>(endpoint: string) => queryOptions({

82

queryKey: ['api', endpoint],

83

queryFn: () => fetch(`/api/${endpoint}`).then(res => res.json()) as Promise<T>,

84

retry: 3,

85

staleTime: 1000 * 60 * 5,

86

});

87

88

// Use reusable configuration

89

const usersQuery = createApiQuery<User[]>('users');

90

const productsQuery = createApiQuery<Product[]>('products');

91

92

// Complex query with multiple options

93

const dashboardQuery = queryOptions({

94

queryKey: ['dashboard', 'summary'],

95

queryFn: async () => {

96

const [users, posts, analytics] = await Promise.all([

97

fetch('/api/users/count').then(r => r.json()),

98

fetch('/api/posts/recent').then(r => r.json()),

99

fetch('/api/analytics/summary').then(r => r.json()),

100

]);

101

return { users, posts, analytics };

102

},

103

select: (data) => ({

104

userCount: data.users.total,

105

recentPosts: data.posts.slice(0, 5),

106

pageViews: data.analytics.pageViews,

107

conversionRate: data.analytics.conversions / data.analytics.visits,

108

}),

109

staleTime: 1000 * 60 * 10,

110

refetchInterval: 1000 * 30, // Refresh every 30 seconds

111

meta: { critical: true },

112

});

113

114

// Conditional query options

115

const conditionalQuery = (enabled: boolean) => queryOptions({

116

queryKey: ['conditional-data'],

117

queryFn: () => fetch('/api/data').then(res => res.json()),

118

enabled,

119

retry: enabled ? 3 : 0,

120

});

121

```

122

123

### infiniteQueryOptions

124

125

Helper function for creating type-safe infinite query options with enhanced TypeScript inference.

126

127

```typescript { .api }

128

/**

129

* Create type-safe infinite query options with enhanced TypeScript inference

130

* @param options - Infinite query options with undefined initial data

131

* @returns Enhanced infinite query options with type information

132

*/

133

function infiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>(

134

options: UndefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>

135

): UndefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam> & {

136

queryKey: DataTag<TQueryKey, InfiniteData<TQueryFnData>, TError>;

137

};

138

139

/**

140

* Create type-safe infinite query options with enhanced TypeScript inference

141

* @param options - Infinite query options with defined initial data

142

* @returns Enhanced infinite query options with type information

143

*/

144

function infiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>(

145

options: DefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>

146

): DefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam> & {

147

queryKey: DataTag<TQueryKey, InfiniteData<TQueryFnData>, TError>;

148

};

149

150

// Base implementation for runtime usage

151

function infiniteQueryOptions(options: unknown): unknown;

152

```

153

154

**Usage Examples:**

155

156

```typescript

157

import { infiniteQueryOptions, useInfiniteQuery } from '@tanstack/vue-query';

158

159

// Basic infinite query options

160

const postsInfiniteQuery = infiniteQueryOptions({

161

queryKey: ['posts', 'infinite'],

162

queryFn: ({ pageParam = 1 }) =>

163

fetch(`/api/posts?page=${pageParam}&limit=10`).then(res => res.json()),

164

initialPageParam: 1,

165

getNextPageParam: (lastPage, allPages) =>

166

lastPage.hasMore ? allPages.length + 1 : undefined,

167

staleTime: 1000 * 60 * 5,

168

});

169

170

// Use with useInfiniteQuery

171

const {

172

data: posts,

173

fetchNextPage,

174

hasNextPage,

175

isFetchingNextPage

176

} = useInfiniteQuery(postsInfiniteQuery);

177

178

// Infinite query with search parameters

179

const searchInfiniteQuery = (searchTerm: string) => infiniteQueryOptions({

180

queryKey: ['search', searchTerm, 'infinite'],

181

queryFn: ({ queryKey, pageParam = 0 }) =>

182

fetch(`/api/search?q=${queryKey[1]}&offset=${pageParam}&limit=20`)

183

.then(res => res.json()),

184

initialPageParam: 0,

185

getNextPageParam: (lastPage, allPages, lastPageParam) =>

186

lastPage.results.length === 20 ? lastPageParam + 20 : undefined,

187

getPreviousPageParam: (firstPage, allPages, firstPageParam) =>

188

firstPageParam > 0 ? Math.max(0, firstPageParam - 20) : undefined,

189

enabled: !!searchTerm && searchTerm.length > 2,

190

staleTime: 1000 * 60 * 2,

191

});

192

193

// Use with reactive search term

194

const searchTerm = ref('');

195

const searchResults = useInfiniteQuery(

196

computed(() => searchInfiniteQuery(searchTerm.value))

197

);

198

199

// Infinite query with cursor-based pagination

200

const cursorInfiniteQuery = infiniteQueryOptions({

201

queryKey: ['timeline'],

202

queryFn: ({ pageParam }) =>

203

fetch(`/api/timeline${pageParam ? `?cursor=${pageParam}` : ''}`)

204

.then(res => res.json()),

205

initialPageParam: undefined,

206

getNextPageParam: (lastPage) => lastPage.nextCursor,

207

getPreviousPageParam: (firstPage) => firstPage.prevCursor,

208

select: (data) => ({

209

pages: data.pages,

210

pageParams: data.pageParams,

211

items: data.pages.flatMap(page => page.items),

212

}),

213

});

214

215

// Complex infinite query with filtering

216

const filteredInfiniteQuery = (category: string, sortBy: string) => infiniteQueryOptions({

217

queryKey: ['products', 'infinite', category, sortBy],

218

queryFn: ({ queryKey, pageParam = 1 }) => {

219

const [, , category, sortBy] = queryKey;

220

return fetch(

221

`/api/products?category=${category}&sort=${sortBy}&page=${pageParam}`

222

).then(res => res.json());

223

},

224

initialPageParam: 1,

225

getNextPageParam: (lastPage, allPages) => {

226

if (lastPage.products.length < lastPage.pageSize) return undefined;

227

return allPages.length + 1;

228

},

229

select: (data) => ({

230

...data,

231

totalProducts: data.pages.reduce((sum, page) => sum + page.products.length, 0),

232

categories: [...new Set(data.pages.flatMap(page =>

233

page.products.map(p => p.category)

234

))],

235

}),

236

staleTime: 1000 * 60 * 5,

237

maxPages: 10, // Limit to prevent excessive memory usage

238

});

239

240

// Infinite query with optimistic updates

241

const commentsInfiniteQuery = (postId: number) => infiniteQueryOptions({

242

queryKey: ['post', postId, 'comments'],

243

queryFn: ({ pageParam = 1 }) =>

244

fetch(`/api/posts/${postId}/comments?page=${pageParam}`)

245

.then(res => res.json()),

246

initialPageParam: 1,

247

getNextPageParam: (lastPage, allPages) =>

248

lastPage.hasMore ? allPages.length + 1 : undefined,

249

select: (data) => ({

250

...data,

251

allComments: data.pages.flatMap(page => page.comments),

252

totalCount: data.pages[0]?.totalCount || 0,

253

}),

254

});

255

256

// Reusable infinite query factory

257

const createInfiniteListQuery = <T>(

258

endpoint: string,

259

pageSize: number = 20

260

) => infiniteQueryOptions({

261

queryKey: [endpoint, 'infinite'],

262

queryFn: ({ pageParam = 0 }) =>

263

fetch(`/api/${endpoint}?offset=${pageParam}&limit=${pageSize}`)

264

.then(res => res.json()) as Promise<{

265

items: T[];

266

hasMore: boolean;

267

total: number;

268

}>,

269

initialPageParam: 0,

270

getNextPageParam: (lastPage, allPages) =>

271

lastPage.hasMore ? allPages.length * pageSize : undefined,

272

staleTime: 1000 * 60 * 5,

273

});

274

275

// Use reusable factory

276

const usersInfiniteQuery = createInfiniteListQuery<User>('users');

277

const ordersInfiniteQuery = createInfiniteListQuery<Order>('orders', 50);

278

```

279

280

## Advanced Patterns

281

282

**Usage Examples:**

283

284

```typescript

285

// Query options with derived data

286

const createDerivedQuery = <TData, TDerived>(

287

baseOptions: Parameters<typeof queryOptions>[0],

288

derive: (data: TData) => TDerived

289

) => queryOptions({

290

...baseOptions,

291

select: (data: TData) => derive(data),

292

});

293

294

const statsQuery = createDerivedQuery(

295

{

296

queryKey: ['raw-stats'],

297

queryFn: () => fetch('/api/stats').then(res => res.json()),

298

},

299

(rawStats) => ({

300

summary: {

301

total: rawStats.reduce((sum, stat) => sum + stat.value, 0),

302

average: rawStats.length > 0 ? rawStats.reduce((sum, stat) => sum + stat.value, 0) / rawStats.length : 0,

303

max: Math.max(...rawStats.map(stat => stat.value)),

304

min: Math.min(...rawStats.map(stat => stat.value)),

305

},

306

byCategory: rawStats.reduce((acc, stat) => {

307

acc[stat.category] = (acc[stat.category] || 0) + stat.value;

308

return acc;

309

}, {}),

310

})

311

);

312

313

// Query options with error boundary

314

const createSafeQuery = <T>(

315

options: Parameters<typeof queryOptions>[0],

316

fallbackData: T

317

) => queryOptions({

318

...options,

319

select: (data) => data || fallbackData,

320

retry: (failureCount, error) => {

321

if (error.status === 404) return false;

322

return failureCount < 3;

323

},

324

throwOnError: false,

325

});

326

327

const safeUserQuery = (userId: number) => createSafeQuery(

328

{

329

queryKey: ['user', userId],

330

queryFn: () => fetch(`/api/users/${userId}`).then(res => res.json()),

331

},

332

{ id: userId, name: 'Unknown User', email: '' }

333

);

334

335

// Prefetch helper using query options

336

const usePrefetch = () => {

337

const queryClient = useQueryClient();

338

339

const prefetchQuery = async (options: ReturnType<typeof queryOptions>) => {

340

await queryClient.prefetchQuery(options);

341

};

342

343

const prefetchInfiniteQuery = async (

344

options: ReturnType<typeof infiniteQueryOptions>

345

) => {

346

await queryClient.prefetchInfiniteQuery(options);

347

};

348

349

return { prefetchQuery, prefetchInfiniteQuery };

350

};

351

352

// Usage with prefetch

353

const { prefetchQuery } = usePrefetch();

354

355

const handleUserHover = (userId: number) => {

356

prefetchQuery(userQuery(userId));

357

};

358

```

359

360

## Types

361

362

```typescript { .api }

363

// Type enhancement for query options

364

type DataTag<TQueryKey, TQueryFnData, TError> = TQueryKey & {

365

_dataType?: TQueryFnData;

366

_errorType?: TError;

367

};

368

369

// Query options variants

370

interface DefinedInitialQueryOptions<TQueryFnData, TError, TData, TQueryKey>

371

extends UseQueryOptions<TQueryFnData, TError, TData, TQueryKey> {

372

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

373

}

374

375

interface UndefinedInitialQueryOptions<TQueryFnData, TError, TData, TQueryKey>

376

extends UseQueryOptions<TQueryFnData, TError, TData, TQueryKey> {

377

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

378

}

379

380

// Infinite query options variants

381

interface DefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>

382

extends UseInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam> {

383

initialData:

384

| InfiniteData<TQueryFnData, TPageParam>

385

| (() => InfiniteData<TQueryFnData, TPageParam>);

386

}

387

388

interface UndefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>

389

extends UseInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam> {

390

initialData?: undefined;

391

}

392

393

// Data structure for infinite queries

394

interface InfiniteData<TData, TPageParam = unknown> {

395

pages: TData[];

396

pageParams: TPageParam[];

397

}

398

399

// Base query key type

400

type QueryKey = ReadonlyArray<unknown>;

401

402

// Error type

403

type DefaultError = Error;

404

```