@atproto/lex-server

中文文档
0.2.3发布于 2026/11/06MITTypeScriptES Module构建来源已验证近 30 天下载 6645

Atproto Lexicon 协议和计划请求路由器

中文使用文档
文档目录28 节
  1. 快速启动
  2. 莱克萨斯路特
  3. 创建路由器
  4. 添加路由
  5. 处理器上下文
  6. 处理器输出
  7. 查询和程序
  8. 查询处理器
  9. 程序处理器
  10. 二进制有效载荷
  11. 订阅
  12. 认证
  13. 自定义认证
  14. WWW- 认证信头
  15. 处理错误
  16. LexServer 错误
  17. LexServer Auth 错误
  18. 处理器召回错误
  19. 节点.js 服务器
  20. 服务( )
  21. 创建 Server ()
  22. 请求收听器 ()
  23. 升级WebSocket ()
  24. 高级使用
  25. 自定义响应对象
  26. 答复信头
  27. 连接信息
  28. 许可证

@ atproto/ lex- server

Atproto Lexicon 协议和计划请求路由器. 见 更改日志 用于版本历史.

npm install @atproto/lex-server
  • 基于 Lexicon 方案的安全请求路由
  • 为查询、程序和WebSocket订阅提供支持
  • 建于网络标准 Request编号 :Response API(跨跑道便携式)
  • 自定义认证及证书通过
  • 优雅的关闭 AsyncDisposable 模式

[大

此软件包目前处于 预览. API 和特性在稳定发布前会有所改变.

这是什么?

构建AT协议服务器需要处理XRPC请求,对照Lexicon schemas验证输入,管理认证,并支持实时订阅. @atproto/lex-server 以 :

  1. 基于 Lexicon 计程器对类型安全处理器的运行请求
  2. 自动验证请求参数和正文
  3. 提供具有定制战略的灵活认证系统
  4. 支持使用后压处理的 WebSocket 订阅
import { LexRouter } from '@atproto/lex-server'
import { serve, upgradeWebSocket } from '@atproto/lex-server/nodejs'
import * as app from './lexicons/app.js'

const router = new LexRouter({ upgradeWebSocket })
  .add(app.bsky.actor.getProfile, async ({ params }) => {
    const profile = await db.getProfile(params.actor)
    return { body: profile }
  })
  .add(app.bsky.feed.post.create, {
    auth: requireAuth,
    handler: async ({ credentials, input }) => {
      const result = await db.createPost(credentials.did, input.body)
      return { body: result }
    },
  })

await serve(router, { port: 3000 })

快速启动

1. 安装软件包

npm install @atproto/lex-server

2. 产生Lexicon计划

使用 @atproto/lex 从 Lexicon 定义生成 TypeScript 计划:

lex install app.bsky.actor.getProfile
lex build

3. 创建路由器并添加处理器

import { LexRouter, LexServerError } from '@atproto/lex-server'
import { serve, upgradeWebSocket } from '@atproto/lex-server/nodejs'
import * as app from './lexicons/app.js'

const router = new LexRouter({ upgradeWebSocket })

// Add a query handler
router.add(app.bsky.actor.getProfile, async ({ params }) => {
  const profile = await db.getProfile(params.actor)
  if (!profile) {
    throw new LexServerError(404, {
      error: 'NotFound',
      message: 'Profile not found',
    })
  }
  return { body: profile }
})

// Start the server
const server = await serve(router, { port: 3000 })
console.log('Server listening on port 3000')

莱克萨斯路特

这个 LexRouter 类是核心 @atproto/lex-server它向基于Lexicon schemas的打字安全处理器传递XRPC请求.

创建路由器

import { LexRouter } from '@atproto/lex-server'
import { upgradeWebSocket } from '@atproto/lex-server/nodejs'

const router = new LexRouter({
  // Required for WebSocket subscriptions (Node.js)
  upgradeWebSocket,

  // Optional: Add logging or error reporting for unexpected errors in handlers
  onHandlerError: ({ error, request, method }) => {
    console.error(`Error in ${method.nsid}:`, error)
  },

  // Optional: WebSocket backpressure settings
  highWaterMark: 250_000, // bytes (default: 250KB)
  lowWaterMark: 50_000, // bytes (default: 50KB)
})

添加路由

使用 .add() 方法,它接受Lexicon的图案和处理器:

// Simple handler (no authentication)
router.add(schema, async ({ params, input, request }) => {
  return { body: result }
})

// Handler with authentication
router.add(schema, {
  auth: async ({ request, params }) => credentials,
  handler: async ({ params, input, credentials, request }) => {
    return { body: result }
  },
})

路由器支持方法链:

const router = new LexRouter()
  .add(app.bsky.actor.getProfile, profileHandler)
  .add(app.bsky.feed.getTimeline, timelineHandler)
  .add(app.bsky.feed.post.create, postHandler)

处理器上下文

处理器接收具有以下属性的上下文对象:

type LexRouterHandlerContext<Method, Credentials> = {
  credentials: Credentials // Result of auth function (undefined if no auth)
  input: InferMethodInput<Method> // Parsed request body (procedures only)
  params: InferMethodParams<Method> // Parsed URL query parameters
  request: Request // Original Web Request object
  connection?: ConnectionInfo // Network connection info
}

处理器输出

处理器可以返回各种输出格式 :

// JSON response (encoding inferred from schema)
return { body: { key: 'value' } }

// With custom encoding
return { encoding: 'text/plain', body: 'Hello, world!' }

// With response headers
return { body: data, headers: { 'x-custom': 'value' } }

// Empty response (200 OK with no body)
return {}

// Custom Response object (full control)
return new Response(body, { status: 201, headers })

// Proxy Response
return fetch('https://example.com/data')

查询和程序

查询处理器

查询手柄 GET 请求和接收 URL 查询字符串中的参数 :

import * as app from './lexicons/app.js'

router.add(app.bsky.actor.getProfile, async ({ params }) => {
  // params.actor is typed and validated
  const profile = await db.getProfile(params.actor)
  return { body: profile }
})

程序处理器

程序处理 POST 请求和接收请求机构:

router.add(app.bsky.feed.post.create, async ({ input }) => {
  // input.body contains the parsed and validated request body
  const post = await db.createPost(input.body)
  return { body: { uri: post.uri, cid: post.cid } }
})

二进制有效载荷

对于接受或返回二进制数据的终点:

// Binary input
router.add(app.example.uploadBlob, async ({ input }) => {
  // input.body is a Request object for streaming
  // input.encoding contains the content-type
  const blob = await input.body.arrayBuffer()
  return { body: { cid: await store(blob) } }
})

// Binary output
router.add(app.example.getBlob, async ({ params }) => {
  const stream = await getBlob(params.cid)
  return {
    encoding: 'application/octet-stream',
    body: stream,
  }
})

订阅

订阅通过WebSocket连接提供实时数据. 处理器是生成消息的同步生成器:

import { LexRouter, LexError } from '@atproto/lex-server'
import { serve, upgradeWebSocket } from '@atproto/lex-server/nodejs'
import { scheduler } from 'node:timers/promises'

const router = new LexRouter({
  upgradeWebSocket, // Required for WebSocket support in nodejs
})

router.add(com.example.stream, async function* ({ params, request }) {
  const { cursor = 0, limit = 10 } = params
  const { signal } = request

  for (let i = 0; i < limit; i++) {
    // Yield messages to the client
    yield com.example.stream.message.$build({
      data: `Message ${cursor + i}`,
      cursor: cursor + i,
    })

    // Wait between messages (respects abort signal)
    await scheduler.wait(1000, { signal })
  }

  // Throwing a LexError closes the connection with an error frame
  throw new LexError('LimitReached', `Limit of ${limit} messages reached`)
})

信件为 CBOR- 编码后作为 WebSocket 二进制框发送 。 路由器处理:

  • WebSocket 升级谈判
  • 后压管理
  • 优雅的连接清理
  • 错误框架编码

认证

自定义认证

认证通过 auth 函数在处理器配置中:

import { LexError, LexServerAuthError } from '@atproto/lex-server'

type Credentials = { did: string; scope: string[] }

const requireAuth = async ({
  request,
}: {
  request: Request
}): Promise<Credentials> => {
  const header = request.headers.get('authorization')
  if (!header?.startsWith('Bearer ')) {
    throw new LexServerAuthError(
      'AuthenticationRequired',
      'Bearer token required',
      {
        Bearer: { realm: 'api' },
      },
    )
  }

  const token = header.slice(7)
  const session = await verifyToken(token)
  if (!session) {
    throw new LexServerAuthError(
      'InvalidToken',
      'Token is invalid or expired',
      {
        Bearer: { realm: 'api', error: 'invalid_token' },
      },
    )
  }

  return { did: session.did, scope: session.scope }
}

// Use with handlers
router.add(app.bsky.feed.post.create, {
  auth: requireAuth,
  handler: async ({ credentials, input }) => {
    // credentials.did is available here
    const post = await db.createPost(credentials.did, input.body)
    return { body: post }
  },
})

认证函数 :

  1. 名为 在此之前 解析请求正文
  2. 接收 params, (中文(简体) ), request,以及 connection 资讯
  3. 应投出 LexServerError (或 LexServerAuthError关于失败
  4. 返回交给处理者的证书

WWW- 认证信头

使用 LexServerAuthError 包括 WWW-Authenticate 错误回复中的标题 :

import { LexServerAuthError } from '@atproto/lex-server'

// Simple Bearer challenge
throw new LexServerAuthError('AuthenticationRequired', 'Login required', {
  Bearer: { realm: 'api' },
})
// WWW-Authenticate: Bearer realm="api"

// Multiple schemes
throw new LexServerAuthError('AuthenticationRequired', 'Auth required', {
  Bearer: { realm: 'api', scope: 'read write' },
  Basic: { realm: 'api' },
})
// WWW-Authenticate: Bearer realm="api", scope="read write", Basic realm="api"

// Token68 format
throw new LexServerAuthError('AuthenticationRequired', 'Auth required', {
  Bearer: 'token68value',
})
// WWW-Authenticate: Bearer token68value

处理错误

LexServer 错误

掷出 LexServerError 返回结构化的 XRPC 错误响应 :

import { LexServerError } from '@atproto/lex-server'

router.add(app.bsky.actor.getProfile, async ({ params }) => {
  try {
    const profile = await db.getProfile(params.actor)
    return { body: profile }
  } catch (cause) {
    throw new LexServerError(
      404,
      {
        error: 'NotFound',
        message: 'Profile not found',
      },
      {
        'cache-control': 'max-age=600',
      },
      { cause },
    )
  }
})

使用 XRPC 格式的错误反应 :

{
  "error": "NotFound",
  "message": "Profile not found"
}

LexServer Auth 错误

LexServerAuthError 扩展 LexServerError 与 WWW-Authenticate 页眉支持 :

import { LexServerAuthError } from '@atproto/lex-server'

throw new LexServerAuthError('AuthenticationRequired', 'Invalid credentials', {
  Bearer: { realm: 'api' },
})

返回与 $401 的响应 WWW-Authenticate 头曰.

处理器召回错误

使用 onHandlerError 以登录或报告出乎意料的错误 :

const router = new LexRouter({
  onHandlerError: async ({ error, request, method }) => {
    // Log errors (excluding expected abort signals)
    console.error(`Error in ${method.nsid}:`, error)
    await reportToSentry(error)
  },
})

[注]

回调只用于意外错误,而不是用于 LexError 实例或请求中止.

节点.js 服务器

这个 @atproto/lex-server/nodejs 子路径提供Node.js特定公用事业.

服务( )

启动服务器并开始监听 :

import { serve } from '@atproto/lex-server/nodejs'

const server = await serve(router, { port: 3000 })
console.log('Server listening on port 3000')

// Graceful shutdown
await server.terminate()

服务器支持 AsyncDisposable数字 :

await using server = await serve(router, { port: 3000 })
// Server is automatically terminated when scope exits

选项 :

type StartServerOptions = {
  port?: number
  host?: string
  gracefulTerminationTimeout?: number // ms to wait for connections to close
}

创建 Server ()

不启动而创建服务器 :

import { createServer } from '@atproto/lex-server/nodejs'

const server = createServer(router, {
  gracefulTerminationTimeout: 5000,
})

server.listen(3000, () => {
  console.log('Server listening')
})

请求收听器 ()

将处理器转换为快递/连接兼容的中间软件 :

import express from 'express'
import { toRequestListener } from '@atproto/lex-server/nodejs'

const app = express()

// Mount the XRPC router
app.use(toRequestListener(router.fetch))

app.listen(3000)

升级WebSocket ()

在节点.js中需要 WebSocket 订阅支持 :

import { LexRouter } from '@atproto/lex-server'
import { upgradeWebSocket } from '@atproto/lex-server/nodejs'

const router = new LexRouter({ upgradeWebSocket })

高级使用

自定义响应对象

还原 a Response 用于完全控制响应的对象(任何类型的功能) 该机构的审定:

router.add(schema, async ({ params }) => {
  if (params.redirect) {
    return Response.redirect('https://example.com', 302)
  }

  return Response.json(
    { custom: true },
    {
      status: 201,
      headers: {
        'X-Custom-Header': 'value',
      },
    },
  )
})

答复信头

在回复中添加标题 :

router.add(schema, async ({ params }) => {
  return {
    body: { data: 'value' },
    headers: {
      'Cache-Control': 'public, max-age=3600',
      'X-Request-Id': crypto.randomUUID(),
    },
  }
})

连接信息

访问网络连接信息 :

router.add(schema, async ({ connection }) => {
  console.log('Remote address:', connection?.remoteAddr?.hostname)
  console.log('Local address:', connection?.localAddr?.hostname)
  return { body: { status: 'ok' } }
})

连接信息结构 :

type ConnectionInfo = {
  localAddr?: {
    hostname: string
    port: number
    transport: 'tcp' | 'udp'
  }
  remoteAddr?: {
    hostname: string
    port: number
    transport: 'tcp' | 'udp'
  }
}

许可证

麻省理工学院或Apachi2

版本与发布记录

@atproto/lex-server 共发布 55 个版本,首次发布于 2026/01/02;过去 12 个月发布了 55 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.2.3latest2026/11/061 天前
0.0.0-spaces-alpha-20261105160032alpha2026/11/062 天前
0.0.0-spaces-alpha-202610291719132026/10/309 天前
0.0.0-spaces-alpha-202610221412282026/10/2216 天前
0.2.02026/10/1721 天前
0.0.0-spaces-alpha-202610151435092026/10/1523 天前

其他发布通道:next → 0.1.0-next.0,alpha → 0.0.0-spaces-alpha-20261105160032。

关于 @atproto/lex-server 的常见问题

@atproto/lex-server 是什么?

Atproto Lexicon 协议和计划请求路由器

如何安装 @atproto/lex-server?

在项目目录运行 npm install @atproto/lex-server,或使用 pnpm add / yarn add / bun add。

@atproto/lex-server 支持 TypeScript 吗?

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

@atproto/lex-server 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@atproto/lex-server 需要什么版本的 Node.js?

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

@atproto/lex-server 可以商用吗?

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

@atproto/lex-server 的下载量有多少?

根据 npm 官方下载统计,@atproto/lex-server 在 2026/10/07 – 2026/11/05 这 30 天内被下载 6,645 次,日均约 222 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@atproto/lex-server 还在维护吗?

最新版本 0.2.3 发布于 2026/11/06(1 天前);至今共发布 55 个版本,其中过去 12 个月发布了 55 个;首次发布于 2026/01/02。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。