0
# Node.js Integration
1
2
SvelteKit provides utilities to bridge between Node.js HTTP objects and Web API Request/Response objects, plus polyfills for web APIs in Node.js environments.
3
4
## Capabilities
5
6
### Request Conversion
7
8
Convert Node.js IncomingMessage to Web API Request.
9
10
```typescript { .api }
11
/**
12
* Converts Node.js IncomingMessage to Web API Request object
13
* @param options - Configuration object
14
* @param options.request - Node.js IncomingMessage
15
* @param options.base - Base URL for the application
16
* @param options.bodySizeLimit - Maximum body size in bytes (optional)
17
* @returns Promise resolving to Web API Request object
18
*/
19
function getRequest(options: {
20
request: import('http').IncomingMessage;
21
base: string;
22
bodySizeLimit?: number;
23
}): Promise<Request>;
24
```
25
26
**Usage Examples:**
27
28
```typescript
29
import { getRequest } from '@sveltejs/kit/node';
30
import { createServer } from 'http';
31
32
const server = createServer(async (req, res) => {
33
try {
34
// Convert Node.js request to Web API Request
35
const request = await getRequest({
36
request: req,
37
base: 'http://localhost:3000',
38
bodySizeLimit: 1024 * 1024 // 1MB limit
39
});
40
41
// Now you can use the Web API Request
42
console.log('Method:', request.method);
43
console.log('URL:', request.url);
44
console.log('Headers:', Object.fromEntries(request.headers));
45
46
if (request.method === 'POST') {
47
const body = await request.text();
48
console.log('Body:', body);
49
}
50
51
} catch (error) {
52
console.error('Request conversion failed:', error);
53
res.statusCode = 400;
54
res.end('Bad Request');
55
}
56
});
57
```
58
59
### Response Conversion
60
61
Apply Web API Response to Node.js ServerResponse.
62
63
```typescript { .api }
64
/**
65
* Applies Web API Response to Node.js ServerResponse
66
* @param res - Node.js ServerResponse object
67
* @param response - Web API Response object
68
* @returns Promise that resolves when response is fully written
69
*/
70
function setResponse(res: import('http').ServerResponse, response: Response): Promise<void>;
71
```
72
73
**Usage Examples:**
74
75
```typescript
76
import { setResponse } from '@sveltejs/kit/node';
77
import { json } from '@sveltejs/kit';
78
import { createServer } from 'http';
79
80
const server = createServer(async (req, res) => {
81
try {
82
// Create a Web API Response
83
const response = json({
84
message: 'Hello from SvelteKit',
85
timestamp: new Date().toISOString()
86
});
87
88
// Apply it to Node.js response
89
await setResponse(res, response);
90
91
} catch (error) {
92
console.error('Response conversion failed:', error);
93
res.statusCode = 500;
94
res.end('Internal Server Error');
95
}
96
});
97
98
server.listen(3000, () => {
99
console.log('Server running on http://localhost:3000');
100
});
101
```
102
103
### File Streaming
104
105
Convert files to ReadableStream for efficient file serving.
106
107
```typescript { .api }
108
/**
109
* Converts a file on disk to a readable stream
110
* @param file - Path to the file
111
* @returns ReadableStream for the file
112
* @since 2.4.0
113
*/
114
function createReadableStream(file: string): ReadableStream;
115
```
116
117
**Usage Examples:**
118
119
```typescript
120
import { createReadableStream } from '@sveltejs/kit/node';
121
import { join } from 'path';
122
123
// In an API endpoint
124
export async function GET({ params }) {
125
const filename = params.filename;
126
const filepath = join('uploads', filename);
127
128
// Check if file exists and is safe to serve
129
if (!isFileAccessible(filepath)) {
130
throw error(404, 'File not found');
131
}
132
133
// Create stream
134
const stream = createReadableStream(filepath);
135
136
return new Response(stream, {
137
headers: {
138
'Content-Type': getMimeType(filename),
139
'Content-Disposition': `attachment; filename="${filename}"`
140
}
141
});
142
}
143
```
144
145
### Web API Polyfills
146
147
Install web API polyfills for Node.js environments.
148
149
```typescript { .api }
150
/**
151
* Make various web APIs available as globals in Node.js:
152
* - crypto (from node:crypto webcrypto)
153
* - File (from node:buffer, Node 18.13+)
154
*/
155
function installPolyfills(): void;
156
```
157
158
**Usage Examples:**
159
160
```typescript
161
// src/hooks.server.js or app.js
162
import { installPolyfills } from '@sveltejs/kit/node/polyfills';
163
164
// Install polyfills at application startup
165
installPolyfills();
166
167
// Now you can use web APIs in Node.js
168
export async function handle({ event, resolve }) {
169
// Use crypto API
170
const hash = await crypto.subtle.digest('SHA-256',
171
new TextEncoder().encode('hello world')
172
);
173
174
// Use File API (Node 18.13+)
175
const file = new File(['content'], 'test.txt', { type: 'text/plain' });
176
177
return resolve(event);
178
}
179
```
180
181
## Integration Patterns
182
183
### Custom Node.js Server
184
185
```typescript
186
// server.js
187
import { installPolyfills } from '@sveltejs/kit/node/polyfills';
188
import { getRequest, setResponse } from '@sveltejs/kit/node';
189
import { Server } from '@sveltejs/kit';
190
import { createServer } from 'http';
191
import { readFileSync } from 'fs';
192
193
// Install polyfills
194
installPolyfills();
195
196
// Load SvelteKit app
197
const manifest = JSON.parse(readFileSync('build/manifest.json', 'utf8'));
198
const app = new Server(manifest);
199
200
await app.init({
201
env: process.env
202
});
203
204
const server = createServer(async (req, res) => {
205
try {
206
// Convert Node.js request to Web API
207
const request = await getRequest({
208
request: req,
209
base: `http://${req.headers.host}`,
210
bodySizeLimit: 10 * 1024 * 1024 // 10MB
211
});
212
213
// Get SvelteKit response
214
const response = await app.respond(request, {
215
getClientAddress: () => {
216
return req.socket.remoteAddress || '127.0.0.1';
217
}
218
});
219
220
// Convert Web API response to Node.js
221
await setResponse(res, response);
222
223
} catch (error) {
224
console.error('Server error:', error);
225
res.statusCode = 500;
226
res.end('Internal Server Error');
227
}
228
});
229
230
const port = process.env.PORT || 3000;
231
server.listen(port, () => {
232
console.log(`Server running on port ${port}`);
233
});
234
```
235
236
### Express.js Integration
237
238
```typescript
239
// server.js
240
import { installPolyfills } from '@sveltejs/kit/node/polyfills';
241
import { getRequest, setResponse } from '@sveltejs/kit/node';
242
import { Server } from '@sveltejs/kit';
243
import express from 'express';
244
245
installPolyfills();
246
247
const app = express();
248
const manifest = JSON.parse(readFileSync('build/manifest.json', 'utf8'));
249
const svelteKit = new Server(manifest);
250
251
await svelteKit.init({ env: process.env });
252
253
// Serve static files
254
app.use(express.static('build/client'));
255
256
// SvelteKit handler
257
app.use(async (req, res, next) => {
258
try {
259
const request = await getRequest({
260
request: req,
261
base: `${req.protocol}://${req.get('host')}`,
262
bodySizeLimit: 10 * 1024 * 1024
263
});
264
265
const response = await svelteKit.respond(request, {
266
getClientAddress: () => req.ip || req.socket.remoteAddress
267
});
268
269
await setResponse(res, response);
270
} catch (error) {
271
next(error);
272
}
273
});
274
275
app.listen(3000);
276
```
277
278
### File Streaming
279
280
Converts a file on disk to a ReadableStream for efficient file serving.
281
282
```typescript { .api }
283
/**
284
* Converts a file on disk to a readable stream
285
* @param file - Path to the file on disk
286
* @returns ReadableStream for the file
287
* @since 2.4.0
288
*/
289
function createReadableStream(file: string): ReadableStream;
290
```
291
292
### Web API Polyfills
293
294
Installs Web API polyfills in Node.js environments.
295
296
```typescript { .api }
297
/**
298
* Installs web API polyfills for Node.js environments
299
* Adds support for crypto, File, FormData, and other web APIs
300
*/
301
function installPolyfills(): void;
302
```
303
304
**Import Path:**
305
306
```typescript
307
import { installPolyfills } from '@sveltejs/kit/node/polyfills';
308
```
309
310
**Usage Examples:**
311
312
```typescript
313
// At application startup
314
import { installPolyfills } from '@sveltejs/kit/node/polyfills';
315
316
installPolyfills();
317
318
// Now you can use web APIs in Node.js
319
const encoder = new TextEncoder();
320
const data = encoder.encode('Hello World');
321
```
322
323
### File Upload with Streams
324
325
```typescript
326
// src/routes/api/upload/+server.js
327
import { createReadableStream } from '@sveltejs/kit/node';
328
import { createWriteStream } from 'fs';
329
import { pipeline } from 'stream/promises';
330
import { join } from 'path';
331
332
export async function POST({ request }) {
333
const contentType = request.headers.get('content-type');
334
335
if (!contentType?.startsWith('multipart/form-data')) {
336
throw error(400, 'Expected multipart/form-data');
337
}
338
339
const formData = await request.formData();
340
const file = formData.get('file');
341
342
if (!file || !(file instanceof File)) {
343
throw error(400, 'No file provided');
344
}
345
346
// Save file using streams for memory efficiency
347
const uploadPath = join('uploads', `${Date.now()}-${file.name}`);
348
const writeStream = createWriteStream(uploadPath);
349
350
try {
351
// Convert File to ReadableStream and pipe to file
352
const reader = file.stream().getReader();
353
const readableStream = new ReadableStream({
354
start(controller) {
355
function pump() {
356
return reader.read().then(({ done, value }) => {
357
if (done) {
358
controller.close();
359
return;
360
}
361
controller.enqueue(value);
362
return pump();
363
});
364
}
365
return pump();
366
}
367
});
368
369
// Use pipeline for proper error handling
370
await pipeline(
371
Readable.fromWeb(readableStream),
372
writeStream
373
);
374
375
return json({
376
message: 'File uploaded successfully',
377
filename: file.name,
378
size: file.size,
379
path: uploadPath
380
});
381
382
} catch (error) {
383
console.error('Upload failed:', error);
384
throw error(500, 'Upload failed');
385
}
386
}
387
```
388
389
### Custom Asset Serving
390
391
```typescript
392
// src/routes/assets/[...path]/+server.js
393
import { createReadableStream } from '@sveltejs/kit/node';
394
import { stat } from 'fs/promises';
395
import { join, extname } from 'path';
396
397
const ASSETS_DIR = 'static/assets';
398
const MIME_TYPES = {
399
'.jpg': 'image/jpeg',
400
'.jpeg': 'image/jpeg',
401
'.png': 'image/png',
402
'.gif': 'image/gif',
403
'.webp': 'image/webp',
404
'.svg': 'image/svg+xml',
405
'.pdf': 'application/pdf',
406
'.txt': 'text/plain'
407
};
408
409
export async function GET({ params, request, setHeaders }) {
410
const path = params.path;
411
const filepath = join(ASSETS_DIR, path);
412
413
// Security: prevent directory traversal
414
if (path.includes('..') || !filepath.startsWith(ASSETS_DIR)) {
415
throw error(403, 'Access denied');
416
}
417
418
try {
419
const stats = await stat(filepath);
420
421
if (!stats.isFile()) {
422
throw error(404, 'File not found');
423
}
424
425
// Check if-modified-since header
426
const ifModifiedSince = request.headers.get('if-modified-since');
427
if (ifModifiedSince) {
428
const modifiedSince = new Date(ifModifiedSince);
429
if (stats.mtime <= modifiedSince) {
430
return new Response(null, { status: 304 });
431
}
432
}
433
434
// Determine content type
435
const ext = extname(filepath).toLowerCase();
436
const contentType = MIME_TYPES[ext] || 'application/octet-stream';
437
438
// Set caching headers
439
setHeaders({
440
'Content-Type': contentType,
441
'Content-Length': stats.size.toString(),
442
'Last-Modified': stats.mtime.toUTCString(),
443
'Cache-Control': 'public, max-age=3600', // 1 hour
444
'ETag': `"${stats.mtime.getTime()}-${stats.size}"`
445
});
446
447
// Create and return stream
448
const stream = createReadableStream(filepath);
449
return new Response(stream);
450
451
} catch (err) {
452
if (err.code === 'ENOENT') {
453
throw error(404, 'File not found');
454
}
455
throw error(500, 'Failed to serve file');
456
}
457
}
458
```
459
460
## Best Practices
461
462
1. **Install polyfills early**: Call `installPolyfills()` at application startup
463
2. **Handle body size limits**: Set appropriate `bodySizeLimit` to prevent memory issues
464
3. **Error handling**: Always wrap conversion calls in try-catch blocks
465
4. **Stream large files**: Use `createReadableStream()` for efficient file serving
466
5. **Security**: Validate file paths and prevent directory traversal attacks
467
6. **Headers**: Preserve important headers during conversion
468
7. **Memory management**: Use streams for large file operations
469
8. **Client IP**: Properly extract client IP addresses from request objects
470
9. **Content types**: Set appropriate MIME types for file responses
471
10. **Caching**: Implement proper caching headers for static assets