or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

app-server.mdapp-state-navigation.mdapp-state.mdconfiguration.mderror-handling.mdform-actions.mdhooks.mdindex.mdload-functions.mdnodejs-integration.mdrequest-handling.mdresponse-creation.mdservice-worker.mdvite-integration.md

request-handling.mddocs/

0

# Request Handling

1

2

SvelteKit provides comprehensive request handling capabilities through the RequestEvent interface, which gives you access to incoming HTTP requests and allows you to interact with cookies, headers, and platform-specific features.

3

4

## Capabilities

5

6

### RequestEvent Interface

7

8

The core interface for handling HTTP requests in SvelteKit.

9

10

```typescript { .api }

11

/**

12

* Request event object available in server load functions, actions, and API endpoints

13

*/

14

interface RequestEvent<Params = Record<string, string>> {

15

/** Cookie management interface */

16

cookies: Cookies;

17

/** Enhanced fetch function with server-side benefits */

18

fetch: typeof fetch;

19

/** Get client IP address */

20

getClientAddress: () => string;

21

/** Server-side locals object for sharing data across handlers */

22

locals: App.Locals;

23

/** Route parameters extracted from URL */

24

params: Params;

25

/** Platform-specific context (adapter-dependent) */

26

platform: Readonly<App.Platform> | undefined;

27

/** The incoming HTTP request */

28

request: Request;

29

/** Route information */

30

route: { id: string };

31

/** Set response headers */

32

setHeaders: (headers: Record<string, string>) => void;

33

/** The request URL */

34

url: URL;

35

/** True if this is a data request (for load functions) */

36

isDataRequest: boolean;

37

/** True if this is an internal subrequest */

38

isSubRequest: boolean;

39

}

40

```

41

42

### Cookie Management

43

44

Interface for managing cookies in server-side code.

45

46

```typescript { .api }

47

interface Cookies {

48

/** Get a cookie value */

49

get: (name: string, opts?: import('cookie').CookieParseOptions) => string | undefined;

50

51

/** Get all cookies */

52

getAll: (opts?: import('cookie').CookieParseOptions) => Array<{ name: string; value: string }>;

53

54

/** Set a cookie */

55

set: (

56

name: string,

57

value: string,

58

opts: import('cookie').CookieSerializeOptions & { path: string }

59

) => void;

60

61

/** Delete a cookie */

62

delete: (name: string, opts: import('cookie').CookieSerializeOptions & { path: string }) => void;

63

64

/** Serialize a cookie without applying it */

65

serialize: (

66

name: string,

67

value: string,

68

opts: import('cookie').CookieSerializeOptions & { path: string }

69

) => string;

70

}

71

```

72

73

## Request Handling Patterns

74

75

### API Endpoints

76

77

```typescript

78

// src/routes/api/users/+server.js

79

import { json, error } from '@sveltejs/kit';

80

81

export async function GET({ url, cookies, locals }) {

82

// Check authentication

83

const sessionId = cookies.get('session');

84

if (!sessionId || !locals.user) {

85

throw error(401, 'Unauthorized');

86

}

87

88

// Parse query parameters

89

const page = parseInt(url.searchParams.get('page') || '1');

90

const limit = parseInt(url.searchParams.get('limit') || '10');

91

92

// Fetch data

93

const users = await getUsersPaginated({ page, limit });

94

95

return json({

96

users,

97

pagination: {

98

page,

99

limit,

100

total: users.total

101

}

102

});

103

}

104

105

export async function POST({ request, cookies, locals }) {

106

if (!locals.user?.isAdmin) {

107

throw error(403, 'Admin access required');

108

}

109

110

const userData = await request.json();

111

112

// Validate input

113

if (!userData.email || !userData.name) {

114

throw error(400, 'Email and name are required');

115

}

116

117

try {

118

const user = await createUser(userData);

119

return json(user, { status: 201 });

120

} catch (err) {

121

if (err.code === 'DUPLICATE_EMAIL') {

122

throw error(409, 'Email already exists');

123

}

124

throw error(500, 'Failed to create user');

125

}

126

}

127

```

128

129

### Cookie Operations

130

131

```typescript

132

// src/routes/auth/login/+server.js

133

export async function POST({ request, cookies }) {

134

const { email, password } = await request.json();

135

136

const user = await authenticate(email, password);

137

if (!user) {

138

throw error(401, 'Invalid credentials');

139

}

140

141

// Set session cookie

142

cookies.set('session', user.sessionId, {

143

path: '/',

144

httpOnly: true,

145

secure: true,

146

sameSite: 'strict',

147

maxAge: 60 * 60 * 24 * 7 // 7 days

148

});

149

150

// Set user preferences cookie (accessible to client)

151

cookies.set('theme', user.preferences.theme, {

152

path: '/',

153

httpOnly: false,

154

secure: true,

155

sameSite: 'strict',

156

maxAge: 60 * 60 * 24 * 365 // 1 year

157

});

158

159

return json({ success: true });

160

}

161

162

// src/routes/auth/logout/+server.js

163

export async function POST({ cookies }) {

164

// Clear session cookie

165

cookies.delete('session', { path: '/' });

166

cookies.delete('theme', { path: '/' });

167

168

return json({ success: true });

169

}

170

```

171

172

### File Upload Handling

173

174

```typescript

175

// src/routes/api/upload/+server.js

176

import { writeFile } from 'fs/promises';

177

import { join } from 'path';

178

179

export async function POST({ request, locals }) {

180

if (!locals.user) {

181

throw error(401, 'Authentication required');

182

}

183

184

const formData = await request.formData();

185

const file = formData.get('file');

186

187

if (!file || !(file instanceof File)) {

188

throw error(400, 'No file uploaded');

189

}

190

191

// Validate file

192

const maxSize = 5 * 1024 * 1024; // 5MB

193

if (file.size > maxSize) {

194

throw error(413, 'File too large');

195

}

196

197

const allowedTypes = ['image/jpeg', 'image/png', 'image/webp'];

198

if (!allowedTypes.includes(file.type)) {

199

throw error(415, 'Unsupported file type');

200

}

201

202

// Save file

203

try {

204

const buffer = Buffer.from(await file.arrayBuffer());

205

const filename = `${Date.now()}-${file.name}`;

206

const filepath = join('uploads', filename);

207

208

await writeFile(filepath, buffer);

209

210

return json({

211

filename,

212

size: file.size,

213

type: file.type,

214

url: `/uploads/${filename}`

215

});

216

} catch (error) {

217

throw error(500, 'Failed to save file');

218

}

219

}

220

```

221

222

### Client IP and Headers

223

224

```typescript

225

// src/routes/api/visitor-info/+server.js

226

export async function GET({ request, getClientAddress, setHeaders }) {

227

const clientIP = getClientAddress();

228

const userAgent = request.headers.get('user-agent');

229

const acceptLanguage = request.headers.get('accept-language');

230

231

// Set CORS headers

232

setHeaders({

233

'Access-Control-Allow-Origin': '*',

234

'Access-Control-Allow-Methods': 'GET',

235

'Cache-Control': 'no-cache'

236

});

237

238

return json({

239

ip: clientIP,

240

userAgent,

241

language: acceptLanguage,

242

timestamp: new Date().toISOString()

243

});

244

}

245

```

246

247

### Request Body Parsing

248

249

```typescript

250

// src/routes/api/webhook/+server.js

251

export async function POST({ request }) {

252

const contentType = request.headers.get('content-type');

253

254

let data;

255

if (contentType?.includes('application/json')) {

256

data = await request.json();

257

} else if (contentType?.includes('application/x-www-form-urlencoded')) {

258

const formData = await request.formData();

259

data = Object.fromEntries(formData);

260

} else if (contentType?.includes('text/')) {

261

data = await request.text();

262

} else {

263

data = await request.arrayBuffer();

264

}

265

266

// Process webhook data

267

await processWebhook(data);

268

269

return json({ received: true });

270

}

271

```

272

273

### Platform-Specific Features

274

275

```typescript

276

// Example with Cloudflare Workers

277

export async function GET({ platform, request }) {

278

if (platform?.env) {

279

// Access Cloudflare Workers environment

280

const kv = platform.env.MY_KV_NAMESPACE;

281

const cached = await kv.get('cached-data');

282

283

if (cached) {

284

return json(JSON.parse(cached));

285

}

286

}

287

288

// Fallback behavior

289

const data = await fetchFreshData();

290

291

if (platform?.env?.MY_KV_NAMESPACE) {

292

// Cache for 1 hour

293

await platform.env.MY_KV_NAMESPACE.put(

294

'cached-data',

295

JSON.stringify(data),

296

{ expirationTtl: 3600 }

297

);

298

}

299

300

return json(data);

301

}

302

```

303

304

## Advanced Request Handling

305

306

### Middleware Pattern with Locals

307

308

```typescript

309

// src/hooks.server.js

310

export async function handle({ event, resolve }) {

311

// Parse session from cookie

312

const sessionId = event.cookies.get('session');

313

if (sessionId) {

314

const user = await getUserBySessionId(sessionId);

315

event.locals.user = user;

316

}

317

318

// Add request ID for logging

319

event.locals.requestId = crypto.randomUUID();

320

321

// Log request

322

console.log(`${event.locals.requestId}: ${event.request.method} ${event.url.pathname}`);

323

324

const response = await resolve(event);

325

326

// Add request ID to response header

327

response.headers.set('X-Request-ID', event.locals.requestId);

328

329

return response;

330

}

331

332

// Now available in all server functions

333

export async function load({ locals }) {

334

console.log(`Request ID: ${locals.requestId}`);

335

336

if (locals.user) {

337

return { user: locals.user };

338

}

339

340

throw redirect(303, '/login');

341

}

342

```

343

344

### Rate Limiting

345

346

```typescript

347

// src/routes/api/limited/+server.js

348

const rateLimits = new Map();

349

350

export async function POST({ getClientAddress, request }) {

351

const clientIP = getClientAddress();

352

const now = Date.now();

353

const windowMs = 60 * 1000; // 1 minute

354

const maxRequests = 10;

355

356

// Clean old entries

357

const cutoff = now - windowMs;

358

const requests = rateLimits.get(clientIP) || [];

359

const recentRequests = requests.filter(time => time > cutoff);

360

361

if (recentRequests.length >= maxRequests) {

362

throw error(429, 'Too many requests');

363

}

364

365

// Record this request

366

recentRequests.push(now);

367

rateLimits.set(clientIP, recentRequests);

368

369

// Process request

370

const data = await request.json();

371

const result = await processLimitedOperation(data);

372

373

return json(result);

374

}

375

```

376

377

### Content Negotiation

378

379

```typescript

380

// src/routes/api/data/+server.js

381

export async function GET({ request, url }) {

382

const accept = request.headers.get('accept') || '';

383

const format = url.searchParams.get('format');

384

385

const data = await getData();

386

387

if (format === 'csv' || accept.includes('text/csv')) {

388

const csv = convertToCSV(data);

389

return new Response(csv, {

390

headers: {

391

'Content-Type': 'text/csv',

392

'Content-Disposition': 'attachment; filename="data.csv"'

393

}

394

});

395

}

396

397

if (format === 'xml' || accept.includes('application/xml')) {

398

const xml = convertToXML(data);

399

return new Response(xml, {

400

headers: { 'Content-Type': 'application/xml' }

401

});

402

}

403

404

// Default to JSON

405

return json(data);

406

}

407

```

408

409

### WebSocket Handling

410

411

```typescript

412

// src/routes/api/websocket/+server.js

413

export async function GET({ request, locals }) {

414

if (!locals.user) {

415

throw error(401, 'Authentication required');

416

}

417

418

const upgrade = request.headers.get('upgrade');

419

if (upgrade !== 'websocket') {

420

throw error(400, 'Expected websocket upgrade');

421

}

422

423

// This is adapter-specific - example for Node.js

424

const { socket, response } = Deno.upgradeWebSocket(request);

425

426

socket.onopen = () => {

427

console.log('WebSocket connection opened');

428

};

429

430

socket.onmessage = (event) => {

431

const message = JSON.parse(event.data);

432

// Handle WebSocket message

433

handleWebSocketMessage(message, locals.user);

434

};

435

436

socket.onclose = () => {

437

console.log('WebSocket connection closed');

438

};

439

440

return response;

441

}

442

```

443

444

## Best Practices

445

446

1. **Validate input thoroughly**: Check all request data before processing

447

2. **Handle errors gracefully**: Use appropriate HTTP status codes and error messages

448

3. **Secure cookie handling**: Use `httpOnly`, `secure`, and appropriate `sameSite` settings

449

4. **Rate limiting**: Implement rate limiting for public APIs

450

5. **Authentication checks**: Verify user permissions before sensitive operations

451

6. **Content type handling**: Parse request bodies based on Content-Type header

452

7. **Response headers**: Set appropriate caching and security headers

453

8. **Logging**: Log requests and errors for debugging and monitoring

454

9. **Platform features**: Leverage adapter-specific features when available

455

10. **Resource cleanup**: Properly handle file uploads and cleanup temporary resources