# @fastify/sse 中文文档

规范地址：https://npmcn.com/package/%40fastify/sse/

版本：0\.6\.1
npm 元数据更新时间：2026-10-18T12:31:02.551Z
中文译文更新时间：2026-11-14T11:16:40.953Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40fastify/sse
原文对照：https://npmcn.com/package/%40fastify/sse/?lang=original
项目主页：https://github.com/fastify/sse#readme
源码仓库：https://github.com/fastify/sse

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：125 kB，29 个文件
- 模块格式：CommonJS
- TypeScript 类型：包内自带
- Node.js 要求：\>=20\.20\.2
- 运行依赖：2 个
- peer 依赖：fastify ^5\.x
- 首次发布：2025-08-26T07:02:12.055Z
- 最新版本发布：2026-10-08T16:07:28.911Z
- 近 30 天下载量：1622389（2026-10-15 至 2026-11-13，npm 官方下载统计）
- 已发布版本数：7（近 12 个月 3 个）

## 中文简介

用于快速化的服务器事件插件

## 安装

```sh
npm install @fastify/sse
```

也可以使用：`pnpm add @fastify/sse`、`yarn add @fastify/sse`、`bun add @fastify/sse`

## README

# @ fastify/ sse (帮助)

[![NPM Version](https://img.shields.io/npm/v/@fastify/sse.svg?style=flat)](https://www.npmjs.com/package/@fastify/sse) [![CI](https://github.com/fastify/sse/workflows/CI/badge.svg)](https://github.com/fastify/sse/actions) [![neostandard javascript style](https://img.shields.io/badge/code_style-neostandard-brightgreen?style=flat)](https://github.com/neostandard/neostandard)

Fastify 的服务器- Sent Evolution 插件 。 提供一等SSE支持,提供干净的API集成,会话管理,和流出能力.

## 特征

-   🚀 **清洁 API**: 路由级 SSE 支持 `{ sse: true }`
-   📡 **流线**: 对 Node.js 流和同步执行器的原生支持
-   🔄 **重新连接**: 内置信件重放 `Last-Event-ID`
-   💓 **心跳动**:可配置的保有机制
-   🎯 **生命周期**: 与快取钩和出错处理完全整合
-   📝 **类型脚本**: 包含完整类型定义
-   ⚡ **业绩**: 高效后压处理

## 安装

```bash
npm i @fastify/sse
```

## 快速启动

```js
const fastify = require('fastify')({ logger: true })

// Register the plugin
await fastify.register(require('@fastify/sse'))

// Create an SSE endpoint
fastify.get('/events', { sse: true }, async (request, reply) => {
  // Send a message
  await reply.sse.send({ data: 'Hello SSE!' })

  // Send with full options
  await reply.sse.send({
    id: '123',
    event: 'update',
    data: { message: 'Hello World' },
    retry: 1000
  })
})

await fastify.listen({ port: 3000 })
```

## API 密码

### 插件注册

```js
await fastify.register(require('@fastify/sse'), {
  // Optional: heartbeat interval in milliseconds (default: 30000)
  heartbeatInterval: 30000,

  // Optional: default serializer (default: JSON.stringify)
  serializer: (data) => JSON.stringify(data)
})
```

### 路由配置

路由选择通过设置 SSE 处理 `sse` 字段。 有三个 表格,每个表格描述路径服务内容:

```js
// SSE-only route: handler always streams. Clients that explicitly refuse
// SSE (e.g. `Accept: application/json`) receive 406 Not Acceptable.
// Ambiguous Accept headers (*/*, text/*, missing) admit SSE per RFC 9110.
fastify.get('/events', { sse: 'only' }, handler)

// Dual-mode route: the same handler serves both SSE and non-SSE clients.
// Only an explicit `text/event-stream` token routes to SSE; everything
// else falls through to the handler with `reply.sse` undefined, so the
// handler is expected to produce a non-SSE response in that branch.
fastify.get('/data', { sse: 'dual' }, handler)

// Manual route: no Accept negotiation. `reply.sse` is always attached and
// the handler decides at runtime whether to stream (by calling reply.sse.*)
// or to return a normal response. Useful for streaming APIs that signal
// streaming via the request body (e.g. OpenAI-style `{ stream: true }`)
// rather than the Accept header.
fastify.post('/v1/chat', { sse: 'manual' }, handler)

// Back-compat: `sse: true` behaves like `'dual'` for routing. If the
// handler is actually SSE-only and trips a TypeError on `reply.sse`
// because of a wildcard Accept, the plugin rethrows with a message
// that names `sse: 'only'` as the fix.
fastify.get('/legacy', { sse: true }, handler)

// Object form (supports per-route options):
fastify.get('/events', {
  sse: {
    kind: 'only',                // 'only' | 'dual' | 'manual' — omit for back-compat
    heartbeat: false,            // Disable heartbeat for this route
    serializer: customSerializer // Custom serializer for this route
  }
}, handler)
```

### 回复( sse. send) (来源)

发送SSE消息. 接受各种源类型 :

#### 单个信件

```js
// Simple data
await reply.sse.send({ data: 'hello' })

// Full SSE message
await reply.sse.send({
  id: '123',
  event: 'update',
  data: { message: 'Hello' },
  retry: 1000
})

// Plain string
await reply.sse.send('plain text message')
```

#### 流源

```js
// Async generator
async function* generateEvents() {
  for (let i = 0; i < 10; i++) {
    yield { id: i, data: `Event ${i}` }
    await sleep(1000)
  }
}
await reply.sse.send(generateEvents())

// Node.js Readable stream
const stream = fs.createReadStream('data.jsonl')
await reply.sse.send(stream)

// Transform existing stream
const transformStream = new Transform({
  transform(chunk, encoding, callback) {
    callback(null, { data: chunk.toString() })
  }
})
someSource.pipe(transformStream)
await reply.sse.send(transformStream)
```

### 回复.se.stream ()

创建用于管道运营的变换流 :

```js
// Use with pipeline for efficient streaming
const { pipeline } = require('stream/promises')
const fs = require('fs')

fastify.get('/file-stream', { sse: true }, async (request, reply) => {
  const fileStream = fs.createReadStream('data.jsonl')

  // Parse each line as JSON and convert to SSE format
  const parseTransform = new Transform({
    transform(chunk, encoding, callback) {
      const lines = chunk.toString().split('\n').filter(Boolean)
      for (const line of lines) {
        try {
          const data = JSON.parse(line)
          this.push({ id: data.id, data })
        } catch (err) {
          // Skip invalid JSON lines
        }
      }
      callback()
    }
  })

  // Stream file data through SSE
  await pipeline(
    fileStream,
    parseTransform,
    reply.sse.stream(),
    reply.raw,
    { end: false }
  )
})
```

### 连接管理

```js
fastify.get('/live', { sse: true }, async (request, reply) => {
  // Keep connection alive (prevents automatic close)
  reply.sse.keepAlive()

  // Send initial message
  await reply.sse.send({ data: 'Connected' })

  // Check if keepAlive was called
  console.log('Keep alive status:', reply.sse.shouldKeepAlive) // true

  // Set up periodic updates
  const interval = setInterval(async () => {
    if (reply.sse.isConnected) {
      await reply.sse.send({ data: 'ping' })
    } else {
      clearInterval(interval)
    }
  }, 1000)

  // Clean up when connection closes
  reply.sse.onClose(() => {
    clearInterval(interval)
    console.log('Connection closed')
  })
})
```

### 信件重放

处理客户端重新连接 `Last-Event-ID`数字 :

```js
const messageHistory = []

fastify.get('/events', { sse: true }, async (request, reply) => {
  // Handle replay on reconnection
  await reply.sse.replay(async (lastEventId) => {
    // Find messages after the last received ID
    const startIndex = messageHistory.findIndex(msg => msg.id === lastEventId)
    const messagesToReplay = startIndex !== -1
      ? messageHistory.slice(startIndex + 1)
      : messageHistory

    // Send missed messages
    for (const message of messagesToReplay) {
      await reply.sse.send(message)
    }
  })

  // Send new message
  const newMessage = { id: Date.now(), data: 'New event' }
  messageHistory.push(newMessage)
  await reply.sse.send(newMessage)
})
```

### 属性和方法

#### 属性

-   `reply.sse.lastEventId`: 客户端最后收到的事件ID(字符串 QQ 无效)
-   `reply.sse.isConnected`连接状态 (布尔)
-   `reply.sse.shouldKeepAlive`: 处理器完成后, 连接是否应该保持活性( boolean)

#### 方法

-   `reply.sse.send(source)`: 从各种来源类型发送 SSE 信件
-   `reply.sse.stream()`: 创建输油管业务转换流
-   `reply.sse.keepAlive()`: 处理器返回后防止自动关闭连接
-   `reply.sse.close()`: 手动关闭连接
-   `reply.sse.replay(callback)`: 使用 Last-Event ID 处理消息重放
-   `reply.sse.onClose(callback)`: 连接关闭时的注册回调
-   `reply.sse.sendHeaders(statusCode?)`: 手动发送信头(称为在首字母上自动发送)

### 自定义响应状态代码

SSE 响应默认为 `200 OK`。要使用不同的状态,请 `reply.code()` 在第一个 SSE 写入之前,或将状态直接传递给一个 手动 `reply.sse.sendHeaders()` 电话 :

```javascript
fastify.get('/events', { sse: 'only' }, async (request, reply) => {
  reply.code(201)
  await reply.sse.send({ data: 'created' })
})

fastify.get('/events-explicit', { sse: 'only' }, async (request, reply) => {
  reply.sse.sendHeaders(201)
  await reply.sse.send({ data: 'created' })
})
```

明确 `sendHeaders()` 参数优先于 `reply.code()`。 。 。 . 一旦第一个 SSE 写入 响应头, 稍后 `reply.code()` 或者说 `sendHeaders()` 电话没有效果.

## 高级使用

### 接受信头谈判

插件解析 `Accept` 每页眉 [RFC 9110 第12.5.1节](https://www.rfc-editor.org/rfc/rfc9110.html#name-accept) 决定是否将请求作为SSE。 决定取决于 路线被宣布类型 :

| 路线类型 | 接受信头 | 行为 |
| --- | --- | --- |
| `sse: 'only'` | `text/event-stream` | 证监会 |
| `sse: 'only'` | `*/*`, (中文(简体) ), `text/*`缺少 | 证监会 |
| `sse: 'only'` | `application/json` 包括:. | **406 无法接受** |
| `sse: 'only'` | `…, text/event-stream;q=0` | **406 无法接受** |
| `sse: 'dual'` | `text/event-stream` | SSE 分支 |
| `sse: 'dual'` | `*/*`, (中文(简体) ), `text/*`缺少 | 非SSE分支 |
| `sse: 'dual'` | `application/json` | 非SSE分支 |
| `sse: 'manual'` | (任何/缺失) | 运行时处理器决定 |
| `sse: true` | (路线与 `'dual'`(中文(简体) ) | \+ 关于滥用的解释性错误 |

简而言之:明确 `text/event-stream` 总是通往SSE; 模糊 通配符到SSE的路由只有在您已经声明了路由 SSE 时才会使用. 一直这样 `curl`, (中文(简体) ), `fetch()`,邮递员默认只与 SSE 合作 结束点,同时保留双模式的预期,即 通配符更偏好更丰富的非SSE代表.

### 回落到定期答复(`sse: 'dual'`(中文(简体) )

双模式路线从一个单一的SSE和非SSE客户端服务 处理者。 插件附加 `reply.sse` 只有客户明确 请求 `text/event-stream`; 否则不使用 `reply.sse` 并且预计将产生正常的反应。 联合国 存在 :

```js
fastify.get('/data', { sse: 'dual' }, async (request, reply) => {
  const data = await getData()

  if (reply.sse) {
    // SSE client — stream the data
    await reply.sse.send({ data })
  } else {
    // Regular client — return JSON
    return { data }
  }
})
```

SSE 特定响应标题( :`Content-Type: text/event-stream` (a) 在第一次审议之前,不得犯 `reply.sse.send()` 编号 : `reply.sse.stream()` 呼叫,所以一个处理器 装饰 `reply.sse` 不过 然后返回掉入到 Fastify 正常值的纯值 串行路径,而不破坏响应.

### 基于请求逻辑的动态流线( R)`sse: 'manual'`(中文(简体) )

一些API根据请求决定是否流 **机构** 相当 比起 `Accept` 头曰. OpenAI 兼容和其他 LLM 端点,用于 示例,返回 a `text/event-stream` 请求提出时的反应 `{ "stream": true }` 和单一的JSON反对——客户端 不发送 `Accept: text/event-stream` 头头左右.

`sse: 'manual'` 盖上这个:它完全跳过接受者谈判, 总是附加 `reply.sse`,并让操作者在运行时决定 是否要流出 。 一样 `'dual'`,SSE信头 懒惰地在 首个 `reply.sse.send()` 编号 : `reply.sse.stream()` 呼叫,所以一个处理器 永不流出 快速化的正常序列.

```js
fastify.post('/v1/chat/completions', { sse: 'manual' }, async (request, reply) => {
  const completion = await model.run(request.body)

  if (request.body.stream) {
    // Stream tokens as SSE — headers commit on the first send().
    await reply.sse.send(completion.tokens) // async iterable / Readable / messages
    return
  }

  // Non-streaming client — return a normal JSON response.
  return completion.toJSON()
})
```

当处理器解决或 掷出(呼叫) `reply.sse.keepAlive()` 保持开放 用于出带 发送,那么 `reply.sse.close()` 完成时). 因为只有心跳 一旦响应作为 SSE 执行, 即非流回落 反应不会被心跳所破坏.

### 处理错误

```js
fastify.get('/stream', { sse: true }, async (request, reply) => {
  try {
    async function* riskyGenerator() {
      yield { data: 'before error' }
      throw new Error('Something went wrong')
    }

    await reply.sse.send(riskyGenerator())
  } catch (error) {
    // Handle errors gracefully
    await reply.sse.send({
      event: 'error',
      data: { message: 'Stream error occurred' }
    })
  }
})
```

### 自定义序列化

```js
// Plugin-level serializer
await fastify.register(require('@fastify/sse'), {
  serializer: (data) => {
    // Custom serialization logic
    return typeof data === 'string' ? data : JSON.stringify(data)
  }
})

// Route-level serializer
fastify.get('/custom', {
  sse: {
    serializer: (data) => `CUSTOM:${JSON.stringify(data)}`
  }
}, async (request, reply) => {
  await reply.sse.send({ data: 'test' }) // Outputs: "CUSTOM:\"test\""
})
```

## 测试

测试SSE端点用标准快活注射简化:

```js
const response = await fastify.inject({
  method: 'GET',
  url: '/events',
  headers: {
    accept: 'text/event-stream'
  }
})

assert.strictEqual(response.statusCode, 200)
assert.strictEqual(response.headers['content-type'], 'text/event-stream')
assert.ok(response.body.includes('data: "Hello SSE!"'))
```

## 客户端用户端

```html
<!DOCTYPE html>
<html>
<head>
  <title>SSE Client</title>
</head>
<body>
  <div id="messages"></div>

  <script>
    const eventSource = new EventSource('/events')
    const messagesDiv = document.getElementById('messages')

    eventSource.onmessage = function(event) {
      const data = JSON.parse(event.data)
      messagesDiv.innerHTML += '<div>' + JSON.stringify(data) + '</div>'
    }

    eventSource.addEventListener('update', function(event) {
      console.log('Update event:', JSON.parse(event.data))
    })

    eventSource.onerror = function(event) {
      console.error('SSE error:', event)
    }
  </script>
</body>
</html>
```

## 类型脚本

全部类型脚本支持包括:

```typescript
import fastify from 'fastify'
import fastifySSE, { SSEMessage } from '@fastify/sse'

const app = fastify()
await app.register(fastifySSE)

app.get('/events', { sse: true }, async (request, reply) => {
  const message: SSEMessage = {
    id: '123',
    event: 'test',
    data: { hello: 'world' }
  }

  await reply.sse.send(message)

  // TypeScript knows about SSE properties
  console.log(reply.sse.isConnected) // boolean
  console.log(reply.sse.lastEventId) // string | null
  console.log(reply.sse.shouldKeepAlive) // boolean
})
```

## 实例

见 [实例](https://npmcn.com/package/%40fastify/sse/examples/) 完整工作示例目录 :

-   [基本用途](https://npmcn.com/package/%40fastify/sse/examples/basic.js) - 简单的SSE终点
-   [动态流](https://npmcn.com/package/%40fastify/sse/examples/manual-streaming.js) - 说吧 `sse: 'manual'`,基于请求正文的 OpenAI 风格的流或 JSON
-   很快会有更多例子出现...

## 与 se- v2 的比较

| 特性 | 钉入- sse- v2 | @ fastify/ sse (帮助) |
| --- | --- | --- |
| 基础SSE | ✅ | ✅ |
| 同步显示器 | ✅ | ✅ |
| 流支持 | ✅ | 增强 |
| 会话管理 | ❌ | ✅ |
| 最后活动ID | ❌ | ✅ |
| 连接保健 | ❌ | ✅ |
| 快速整合 | 有限 | 满分 |
| 测试支持 | ❌ | ✅ |

## 许可证

[麻省理工学院](https://npmcn.com/package/%40fastify/sse/LICENSE)

## 常见问题

### @fastify/sse 是什么？

用于快速化的服务器事件插件

### 如何安装 @fastify/sse？

在项目目录运行 npm install @fastify/sse，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：fastify（^5\.x）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @fastify/sse 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @fastify/sse 支持 ESM 还是 CommonJS？

以 CommonJS 形式发布，可以直接 require\(\)；在 ES Module 项目中也可以用 import 引入（Node\.js 会把 module\.exports 作为默认导出）。

### @fastify/sse 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=20\.20\.2。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @fastify/sse 可以商用吗？

@fastify/sse 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @fastify/sse 的下载量有多少？

根据 npm 官方下载统计，@fastify/sse 在 2026/10/15 – 2026/11/13 这 30 天内被下载 1,622,389 次，日均约 54,080 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @fastify/sse 还在维护吗？

最新版本 0\.6\.1 发布于 2026/10/09（1 个月前）；至今共发布 7 个版本，其中过去 12 个月发布了 3 个；首次发布于 2025/08/26。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

