or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.mdinternal-helpers.mdnodejs-integration.mdstreaming-rendering.mdstring-rendering.mdweb-streams.md

nodejs-integration.mddocs/

0

# Node.js Integration

1

2

Native Node.js stream integration for traditional server environments and frameworks. These functions provide seamless integration with Node.js's built-in stream system, making it easy to integrate Vue SSR into existing Node.js applications and frameworks.

3

4

## Capabilities

5

6

### renderToNodeStream

7

8

Creates a Node.js Readable stream that outputs the rendered HTML. This function is only available in the CommonJS build and provides direct integration with Node.js stream ecosystem.

9

10

```typescript { .api }

11

/**

12

* Renders input as a Node.js Readable stream

13

* @param input - Vue application instance or VNode to render

14

* @param context - Optional SSR context for teleports and additional data

15

* @returns Node.js Readable stream containing the rendered HTML

16

* @throws Error if used in ESM build - use pipeToNodeWritable instead

17

*/

18

function renderToNodeStream(

19

input: App | VNode,

20

context?: SSRContext

21

): Readable;

22

```

23

24

**Usage Examples:**

25

26

```typescript

27

import { createSSRApp } from "vue";

28

import { renderToNodeStream } from "@vue/server-renderer";

29

import { pipeline } from "stream";

30

31

const app = createSSRApp({

32

template: `

33

<div>

34

<h1>Node.js Streaming</h1>

35

<p>Content streamed via Node.js Readable</p>

36

</div>

37

`,

38

});

39

40

// Direct streaming to HTTP response

41

import express from "express";

42

const server = express();

43

44

server.get("/", (req, res) => {

45

res.setHeader('Content-Type', 'text/html');

46

47

const stream = renderToNodeStream(app);

48

stream.pipe(res);

49

50

// Handle errors

51

stream.on('error', (err) => {

52

console.error('Streaming error:', err);

53

res.status(500).end('Internal Server Error');

54

});

55

});

56

57

// Pipeline with transforms

58

import { Transform } from "stream";

59

60

const htmlWrapper = new Transform({

61

objectMode: false,

62

transform(chunk, encoding, callback) {

63

// Wrap content in HTML document

64

if (!this.headerSent) {

65

this.push('<!DOCTYPE html><html><head><title>My App</title></head><body>');

66

this.headerSent = true;

67

}

68

this.push(chunk);

69

callback();

70

},

71

flush(callback) {

72

this.push('</body></html>');

73

callback();

74

}

75

});

76

77

pipeline(

78

renderToNodeStream(app),

79

htmlWrapper,

80

process.stdout,

81

(err) => {

82

if (err) console.error('Pipeline error:', err);

83

else console.log('Pipeline complete');

84

}

85

);

86

```

87

88

### renderToStream (deprecated)

89

90

Legacy function that creates a Node.js Readable stream. This function is deprecated and should be replaced with `renderToNodeStream`.

91

92

```typescript { .api }

93

/**

94

* @deprecated Use renderToNodeStream instead

95

* Renders input as a Node.js Readable stream

96

* @param input - Vue application instance or VNode to render

97

* @param context - Optional SSR context for teleports and additional data

98

* @returns Node.js Readable stream containing the rendered HTML

99

*/

100

function renderToStream(

101

input: App | VNode,

102

context?: SSRContext

103

): Readable;

104

```

105

106

**Migration:**

107

108

```typescript

109

// Old (deprecated)

110

const stream = renderToStream(app, context);

111

112

// New (recommended)

113

const stream = renderToNodeStream(app, context);

114

```

115

116

### pipeToNodeWritable

117

118

Pipes the rendered output directly to an existing Node.js Writable stream. This function works in both CommonJS and ESM builds and provides more control over the destination.

119

120

```typescript { .api }

121

/**

122

* Render and pipe to an existing Node.js Writable stream instance

123

* @param input - Vue application instance or VNode to render

124

* @param context - Optional SSR context for teleports and additional data

125

* @param writable - Node.js Writable stream to pipe the output to

126

*/

127

function pipeToNodeWritable(

128

input: App | VNode,

129

context?: SSRContext,

130

writable: Writable

131

): void;

132

```

133

134

**Usage Examples:**

135

136

```typescript

137

import { createSSRApp } from "vue";

138

import { pipeToNodeWritable } from "@vue/server-renderer";

139

import { createWriteStream } from "fs";

140

import { Transform } from "stream";

141

142

const app = createSSRApp({

143

template: `

144

<div>

145

<h1>Piped Content</h1>

146

<p>This is piped to a writable stream</p>

147

</div>

148

`,

149

});

150

151

// Pipe to file

152

const fileStream = createWriteStream('output.html');

153

pipeToNodeWritable(app, {}, fileStream);

154

155

// Pipe to HTTP response

156

import express from "express";

157

const server = express();

158

159

server.get("/", (req, res) => {

160

res.setHeader('Content-Type', 'text/html');

161

pipeToNodeWritable(app, {}, res);

162

});

163

164

// Pipe through transform streams

165

const compressionStream = new Transform({

166

transform(chunk, encoding, callback) {

167

// Add compression or other transformations

168

const compressed = this.compress(chunk);

169

callback(null, compressed);

170

}

171

});

172

173

pipeToNodeWritable(app, {}, compressionStream);

174

compressionStream.pipe(process.stdout);

175

```

176

177

### SSR Context with Node.js Streams

178

179

Both functions support SSR context for handling teleports and passing data:

180

181

```typescript

182

import { createSSRApp } from "vue";

183

import { pipeToNodeWritable } from "@vue/server-renderer";

184

185

const app = createSSRApp({

186

template: `

187

<div>

188

<h1>Main Content</h1>

189

<Teleport to="#sidebar">

190

<div>Sidebar content</div>

191

</Teleport>

192

</div>

193

`,

194

});

195

196

const context = {

197

userAgent: req.headers['user-agent'],

198

requestId: generateRequestId(),

199

};

200

201

// Custom writable that handles teleports

202

class TeleportAwareWritable extends Writable {

203

constructor(private response: Response) {

204

super();

205

this.chunks = [];

206

}

207

208

_write(chunk, encoding, callback) {

209

this.chunks.push(chunk);

210

callback();

211

}

212

213

_final(callback) {

214

// Combine main content with teleports

215

const mainContent = Buffer.concat(this.chunks).toString();

216

217

let fullHtml = '<!DOCTYPE html><html><body>';

218

fullHtml += mainContent;

219

220

// Add teleported content

221

if (context.teleports) {

222

for (const [target, content] of Object.entries(context.teleports)) {

223

fullHtml += `<div id="${target.slice(1)}">${content}</div>`;

224

}

225

}

226

227

fullHtml += '</body></html>';

228

229

this.response.end(fullHtml);

230

callback();

231

}

232

}

233

234

const writable = new TeleportAwareWritable(res);

235

pipeToNodeWritable(app, context, writable);

236

```

237

238

## Framework Integration Examples

239

240

### Express.js Integration

241

242

```typescript

243

import express from "express";

244

import { createSSRApp } from "vue";

245

import { renderToNodeStream, pipeToNodeWritable } from "@vue/server-renderer";

246

247

const app = express();

248

249

// Using renderToNodeStream

250

app.get("/stream", (req, res) => {

251

const vueApp = createSSRApp({

252

template: `<div>Hello from Express + Vue SSR!</div>`

253

});

254

255

res.setHeader('Content-Type', 'text/html');

256

const stream = renderToNodeStream(vueApp);

257

258

stream.on('error', (err) => {

259

console.error('SSR Error:', err);

260

res.status(500).end('Server Error');

261

});

262

263

stream.pipe(res);

264

});

265

266

// Using pipeToNodeWritable (preferred for ESM)

267

app.get("/pipe", (req, res) => {

268

const vueApp = createSSRApp({

269

template: `<div>Hello from Express + Vue SSR (piped)!</div>`

270

});

271

272

res.setHeader('Content-Type', 'text/html');

273

pipeToNodeWritable(vueApp, {}, res);

274

});

275

```

276

277

### Fastify Integration

278

279

```typescript

280

import Fastify from "fastify";

281

import { createSSRApp } from "vue";

282

import { pipeToNodeWritable } from "@vue/server-renderer";

283

284

const fastify = Fastify({ logger: true });

285

286

fastify.get("/", async (request, reply) => {

287

const vueApp = createSSRApp({

288

template: `<div>Hello from Fastify + Vue SSR!</div>`

289

});

290

291

reply.type('text/html');

292

pipeToNodeWritable(vueApp, {}, reply.raw);

293

});

294

```

295

296

### Koa Integration

297

298

```typescript

299

import Koa from "koa";

300

import { createSSRApp } from "vue";

301

import { pipeToNodeWritable } from "@vue/server-renderer";

302

303

const app = new Koa();

304

305

app.use(async (ctx) => {

306

const vueApp = createSSRApp({

307

template: `<div>Hello from Koa + Vue SSR!</div>`

308

});

309

310

ctx.type = 'text/html';

311

pipeToNodeWritable(vueApp, {}, ctx.res);

312

});

313

```

314

315

## Advanced Usage

316

317

### Custom Error Handling

318

319

```typescript

320

import { pipeToNodeWritable } from "@vue/server-renderer";

321

import { Writable } from "stream";

322

323

class ErrorHandlingWritable extends Writable {

324

constructor(private response: Response) {

325

super();

326

}

327

328

_write(chunk, encoding, callback) {

329

try {

330

this.response.write(chunk);

331

callback();

332

} catch (err) {

333

callback(err);

334

}

335

}

336

337

_final(callback) {

338

this.response.end();

339

callback();

340

}

341

}

342

343

const writable = new ErrorHandlingWritable(res);

344

writable.on('error', (err) => {

345

console.error('Streaming error:', err);

346

if (!res.headersSent) {

347

res.status(500).end('Internal Server Error');

348

}

349

});

350

351

pipeToNodeWritable(app, {}, writable);

352

```

353

354

### Performance Monitoring

355

356

```typescript

357

import { performance } from "perf_hooks";

358

import { pipeToNodeWritable } from "@vue/server-renderer";

359

360

const startTime = performance.now();

361

let firstChunkTime: number;

362

let chunkCount = 0;

363

364

const monitoringWritable = new Writable({

365

write(chunk, encoding, callback) {

366

if (chunkCount === 0) {

367

firstChunkTime = performance.now();

368

console.log(`Time to first chunk: ${firstChunkTime - startTime}ms`);

369

}

370

371

chunkCount++;

372

this.push(chunk);

373

callback();

374

},

375

final(callback) {

376

const endTime = performance.now();

377

console.log(`Total render time: ${endTime - startTime}ms`);

378

console.log(`Total chunks: ${chunkCount}`);

379

callback();

380

}

381

});

382

383

pipeToNodeWritable(app, {}, monitoringWritable);

384

```

385

386

## Environment Considerations

387

388

### CommonJS vs ESM

389

390

- **renderToNodeStream**: Only available in CommonJS build

391

- **pipeToNodeWritable**: Available in both CommonJS and ESM builds

392

- **Recommendation**: Use `pipeToNodeWritable` for maximum compatibility

393

394

### Memory Usage

395

396

Node.js streaming provides excellent memory efficiency:

397

- Content is processed and sent immediately

398

- No buffering of complete HTML in memory

399

- Suitable for large applications with complex component trees

400

401

### Error Recovery

402

403

Node.js streams provide robust error handling mechanisms:

404

- Use stream error events for graceful degradation

405

- Implement timeout handling for slow-rendering components

406

- Consider circuit breaker patterns for problematic components