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