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

nodejs-integration.mddocs/

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