or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.mdparsers.mdquery-states.mdserialization.mdserver-cache.md

server-cache.mddocs/

0

# Server-Side Cache

1

2

Server-side utilities for accessing and parsing query parameters in React Server Components, providing type-safe query parameter handling with request-scoped caching.

3

4

## Capabilities

5

6

### createSearchParamsCache

7

8

Creates a request-scoped cache for parsing and accessing query parameters in server components.

9

10

```typescript { .api }

11

/**

12

* Creates a search params cache for server-side query parameter parsing

13

* @param parsers - Object mapping keys to their respective parsers

14

* @returns Cache instance with parse, get, and all methods

15

*/

16

function createSearchParamsCache<Parsers extends Record<string, ParserBuilder<any>>>(

17

parsers: Parsers

18

): SearchParamsCache<Parsers>;

19

20

interface SearchParamsCache<Parsers extends Record<string, ParserBuilder<any>>> {

21

/** Parse the incoming searchParams page prop using the parsers provided */

22

parse(searchParams: SearchParams): ParsedSearchParams<Parsers>;

23

parse(searchParams: Promise<SearchParams>): Promise<ParsedSearchParams<Parsers>>;

24

25

/** Get a single parsed value by key */

26

get<Key extends keyof Parsers>(key: Key): ParsedSearchParams<Parsers>[Key];

27

28

/** Get all parsed values */

29

all(): ParsedSearchParams<Parsers>;

30

}

31

32

type SearchParams = Record<string, string | string[] | undefined>;

33

34

type ParsedSearchParams<Parsers extends Record<string, ParserBuilder<any>>> = {

35

readonly [K in keyof Parsers]: inferParserType<Parsers[K]>;

36

};

37

38

type inferParserType<Input> =

39

Input extends ParserBuilder<any>

40

? Input extends ParserBuilder<any> & { defaultValue: infer Type }

41

? Type

42

: ReturnType<Input['parse']> | null

43

: never;

44

```

45

46

**Usage Examples:**

47

48

```typescript

49

// app/search/page.tsx

50

import { createSearchParamsCache, parseAsString, parseAsInteger, parseAsBoolean } from "nuqs/server";

51

52

// Define the search params schema

53

const searchParamsCache = createSearchParamsCache({

54

q: parseAsString,

55

page: parseAsInteger.withDefault(1),

56

category: parseAsString,

57

featured: parseAsBoolean.withDefault(false)

58

});

59

60

export default async function SearchPage({

61

searchParams

62

}: {

63

searchParams: Promise<{ [key: string]: string | string[] | undefined }>

64

}) {

65

// Parse all query parameters (Next.js 15+ with Promise)

66

const { q, page, category, featured } = await searchParamsCache.parse(searchParams);

67

68

// Types are fully inferred:

69

// q: string | null

70

// page: number (always defined due to default)

71

// category: string | null

72

// featured: boolean (always defined due to default)

73

74

return (

75

<div>

76

<h1>Search Results</h1>

77

{q && <p>Searching for: {q}</p>}

78

<p>Page: {page}</p>

79

{category && <p>Category: {category}</p>}

80

<p>Featured only: {featured}</p>

81

</div>

82

);

83

}

84

85

// For Next.js 14 and earlier (synchronous searchParams)

86

export default function SearchPage({

87

searchParams

88

}: {

89

searchParams: { [key: string]: string | string[] | undefined }

90

}) {

91

const { q, page, category, featured } = searchParamsCache.parse(searchParams);

92

// ... rest of component

93

}

94

```

95

96

### Individual Parameter Access

97

98

Access individual parsed parameters without parsing the entire object.

99

100

```typescript { .api }

101

/**

102

* Get a single parsed value by key

103

* @param key - The key to retrieve from the cache

104

* @throws Error if parse() hasn't been called first or key doesn't exist

105

*/

106

get<Key extends keyof Parsers>(key: Key): ParsedSearchParams<Parsers>[Key];

107

108

/**

109

* Get all parsed values from the cache

110

* @throws Error if parse() hasn't been called first

111

*/

112

all(): ParsedSearchParams<Parsers>;

113

```

114

115

**Usage Examples:**

116

117

```typescript

118

// In a server component after calling parse()

119

export default async function ProductPage({ searchParams }: { searchParams: Promise<any> }) {

120

// Parse all parameters first

121

await searchParamsCache.parse(searchParams);

122

123

// Access individual values in child components

124

return (

125

<div>

126

<SearchFilters />

127

<ProductList />

128

</div>

129

);

130

}

131

132

// Child server component can access individual values

133

function SearchFilters() {

134

const query = searchParamsCache.get('q');

135

const category = searchParamsCache.get('category');

136

137

return (

138

<div>

139

<p>Current search: {query}</p>

140

<p>Category: {category}</p>

141

</div>

142

);

143

}

144

145

// Or access all values

146

function DebugInfo() {

147

const allParams = searchParamsCache.all();

148

return <pre>{JSON.stringify(allParams, null, 2)}</pre>;

149

}

150

```

151

152

### generateMetadata Integration

153

154

Use the cache in generateMetadata functions for SEO and page metadata.

155

156

```typescript

157

import { Metadata } from "next";

158

159

export async function generateMetadata({

160

searchParams

161

}: {

162

searchParams: Promise<any>

163

}): Promise<Metadata> {

164

const { q, category } = await searchParamsCache.parse(searchParams);

165

166

const title = q

167

? `Search results for "${q}"${category ? ` in ${category}` : ''}`

168

: 'Search';

169

170

return {

171

title,

172

description: `Find products${category ? ` in ${category}` : ''}`

173

};

174

}

175

```

176

177

### Advanced Parser Configuration

178

179

The cache works with all parser types and configurations.

180

181

```typescript

182

import {

183

createSearchParamsCache,

184

parseAsStringEnum,

185

parseAsArrayOf,

186

parseAsJson

187

} from "nuqs/server";

188

189

// Complex parser configuration

190

const advancedCache = createSearchParamsCache({

191

// Enum with default

192

status: parseAsStringEnum(['draft', 'published', 'archived'] as const)

193

.withDefault('published'),

194

195

// Array of strings

196

tags: parseAsArrayOf(parseAsString),

197

198

// JSON object

199

filters: parseAsJson<{

200

minPrice: number;

201

maxPrice: number;

202

brand: string;

203

}>(),

204

205

// Date range

206

startDate: parseAsIsoDateTime,

207

endDate: parseAsIsoDateTime,

208

});

209

210

export default async function AdvancedSearchPage({

211

searchParams

212

}: {

213

searchParams: Promise<any>

214

}) {

215

const params = await advancedCache.parse(searchParams);

216

217

// All types are properly inferred:

218

// params.status: 'draft' | 'published' | 'archived'

219

// params.tags: string[] | null

220

// params.filters: { minPrice: number; maxPrice: number; brand: string } | null

221

// params.startDate: Date | null

222

// params.endDate: Date | null

223

}

224

```

225

226

### Request Scoping and Caching

227

228

The cache is automatically scoped to each request using React's `cache` function.

229

230

```typescript

231

// The same cache instance can be used across multiple server components

232

// in the same request without re-parsing

233

234

// layout.tsx

235

export default async function Layout({

236

searchParams

237

}: {

238

searchParams: Promise<any>

239

}) {

240

await searchParamsCache.parse(searchParams);

241

242

return (

243

<div>

244

<Header />

245

<Sidebar />

246

<main>{children}</main>

247

</div>

248

);

249

}

250

251

// components/Header.tsx

252

function Header() {

253

const query = searchParamsCache.get('q'); // Uses cached value

254

return <SearchInput defaultValue={query} />;

255

}

256

257

// components/Sidebar.tsx

258

function Sidebar() {

259

const category = searchParamsCache.get('category'); // Uses cached value

260

return <CategoryFilter selected={category} />;

261

}

262

```

263

264

### Error Handling

265

266

The cache includes built-in error handling for common scenarios.

267

268

```typescript

269

// Error cases:

270

try {

271

// Throws if parse() hasn't been called first

272

const value = searchParamsCache.get('q');

273

} catch (error) {

274

console.error('Must call parse() before accessing values');

275

}

276

277

try {

278

// Throws if key doesn't exist in parser schema

279

const value = searchParamsCache.get('nonexistent');

280

} catch (error) {

281

console.error('Key not found in parser schema');

282

}

283

284

// Invalid query values are gracefully parsed as null

285

const params = await searchParamsCache.parse({

286

page: 'invalid-number', // Will be parsed as null

287

enabled: 'maybe' // Will be parsed as null (not 'true'/'false')

288

});

289

```