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