API
后端使用 Func 构建站点后端能力

使用 Func 构建站点后端能力

面向 AI 编程智能体的 Func 总览:适用范围、文件与方法、运行时规则、ctx 能力全貌、返回值与错误、鉴权与密钥、SSR 边界与验收流程。JSON 表、资源上传、超时与流式各有专题文档。

Func 是 Talizen / Talizen 的项目级后端运行时,用于把必须在服务端完成的小型业务流程放进站点项目:受保护的数据写入、预约与候补、用户资料更新、第三方 API、Webhook、支付对接,以及需要密钥或流式输出的 AI 请求。

这一篇是总览:适用范围、文件与方法、运行时规则、ctx 能力全貌、返回值与错误、鉴权与密钥、SSR 边界和验收流程。数据表、资源上传、超时与流式这些需要展开讲的主题各有一篇专题文档,见下方专题文档

适用范围

适合 Func

预约、候补、RSVP、线索分发、资料更新、可用性检查、登录后操作、第三方 API、Webhook、支付和简单 JSON 数据读写。

优先用现成能力

普通内容展示使用 CMS;静态联系表单使用 talizen/form;登录界面与会话状态使用 talizen/auth

不适合 Func

脱离请求继续执行的后台任务、重型文件处理、无限流、定时器轮询、自建账号/会话系统、OAuth 回调或令牌交换。

项目隔离

Func 天然属于当前项目。输入、代码和分支逻辑中都不应出现 project_idsite_id 或内部表 ID。

文件、键与方法

Func 文件位于 /backend/func。文件的无扩展名路径就是函数键:/backend/func/booking.ts 对应 booking/backend/func/profile/settings.ts 对应 profile/settings

点号保留给导出方法。单一操作可导出 main;相关操作应从同一文件直接导出多个方法,不要自行编写分发器。

// /backend/func/booking.ts
import type { TalizenFuncContext } from 'talizen/func-runtime'

export function create(input, ctx: TalizenFuncContext) {
  if (!input?.startAt) throw new Error('startAt is required')
  const user = ctx.auth.requireUser()
  return ctx.db.insert('appointments', {
    startAt: input.startAt,
    userId: user.id,
  })
}

export function availability(input, ctx: TalizenFuncContext) {
  return ctx.db.query('appointments', { where: { day: input.day } })
}
invoke('booking.create', input)       // key booking, method create
invoke('profile/settings.update', input)
invoke('booking', input)              // key booking, method main

运行时与代码规则

  • 使用 ESM 导出:export function method(input, ctx);只在需要时从 talizen/func-runtime 导入 TypeScript 类型。
  • 所有平台能力都从 ctx 获取;不要使用旧式全局变量 datadbauthcache。沙箱没有模块加载器,任何值导入都会失败。
  • 在 Func 内验证、裁剪和规范化全部输入。预期业务状态返回结构化 JSON;无效请求或意外故障才抛出错误。
  • Func 不是完整 Node.js 运行时。不要依赖 Node 内置模块;计时器 setTimeout/setInterval 不受支持,不能用于等待、轮询或重试。
  • 标准 fetchResponseTextDecoder 与 Web Crypto 可用于第三方 HTTP、响应读取和签名校验。
  • 对称加解密通过全局 crypto 使用 Web Crypto:AES-GCMAES-CBC 都受支持,AES-CBC 会自动进行兼容的 PKCS#7 填充,示例见 支付宝接入;不要导入 node:crypto

ctx 能力速查

数据

ctx.db.get/query/insert/update/delete 操作项目 JSON 表,返回 { total, list, limit } 或单条记录。查询语法与写入规则 →

鉴权

ctx.auth.currentUser() 读取当前用户;ctx.auth.requireUser() 在未登录时直接拒绝。

用户目录

ctx.users.find/query 按标识符找人或分页翻名单,作用域是全项目,不是当前调用者。查询用户与必须自写的门禁 →

缓存

ctx.cache.get/set/del/incr/expire 适合短期结果、计数器与过期状态,不能替代持久化表。

请求与响应

ctx.request.host/ip/method/path 提供请求信息;原始请求体可按 Fetch 语义读取。使用 ctx.response.status(code) 设置状态码。

通过 ctx.cookies 读取、设置或删除 Cookie。SSE 第一个事件发送后响应头已提交,不能再修改 Cookie。

资源

ctx.assets.upload({ filename, mimeType, base64 }) 上传 Func 内生成的文件。两条上传路径怎么选 →

邮件

ctx.email.send/sendCode/verifyCode 需要先接入邮件集成,凭据不下发到沙箱。使用集成发送邮件 →

流式事件

ctx.sse.send(event, data) 发送有界 SSE 事件;平台负责最终 doneerror 事件。超时与流式 →

诊断

使用 console.log/warn/error 输出日志,并使用 ctx.trace_id 关联一次调用,避免记录密钥与敏感正文。

专题文档

下面几个主题在实现时需要展开的细节较多,各自单独成篇:

JSON 表:定义、读写与查询 →

建表文件格式与校验、记录形状、wherefilter 的算子和陷阱、分页排序、合并式更新,以及表属于 project 而非站点版本这条容易踩空的边界。

上传文件:直传与 Func 内生成 →

浏览器文件走 CDN 签名直传,Func 内生成的字节走 ctx.assets.upload,以及为什么不能用 base64 中转。

超时配置与流式响应 →

timeoutMs 该设在哪、context deadline exceeded 的诊断顺序,以及原生 Fetch + SSE 的完整解析写法。

接入支付宝电脑网站支付 →

公钥模式、RSA2、可选 AES 内容加密、订单表、异步通知验签与幂等更新的完整示例。

返回值与错误

Func HTTP 层返回 { "result": ... }{ "error": "..." }。浏览器里的 invoke() 会解包成功的 result,失败时抛出 TalizenFuncError。业务上可预期的"无库存""重复提交"等状态建议作为明确对象返回。

import { invoke, TalizenFuncError } from 'talizen/func'

try {
  const booking = await invoke('booking.create', input)
} catch (error) {
  const message = error instanceof TalizenFuncError
    ? error.message
    : '提交失败,请稍后重试'
}

注意方向不对称:浏览器侧拿到的是 TalizenFuncError 实例,而 Func 内部ctx.db 等平台能力抛出的是字符串,不是 Error 对象——服务端 catch (e)e.messageundefined,要用 String(e)

返回原始 HTTP Response

普通对象继续使用 JSON 包装;Webhook、支付回调等需要精确响应正文时,直接返回 Func 运行时提供的全局 Response。它会绕过 { result: ... } 包装,并使用指定的状态码、Headers 与正文。

export async function notify(_input, ctx) {
  await verifyWebhook(await ctx.request.text())

  return new Response('success', {
    status: 200,
    headers: { 'Content-Type': 'text/plain; charset=utf-8' },
  })
}

Response 构造器是全局能力,不需要导入。需要显式类型标注时,talizen/func-runtime 提供 type-only 的 ResponseResponseInit。返回原始 Response 的方法应使用原生 fetch() 调用,不要使用会解包 JSON 的 invoke()

JSON、表单、文本和二进制正文都能到达 Func。非 JSON 请求的 input{};用只能读取一次的 ctx.request.text()arrayBuffer() 获取原始正文,验签前不要重新编码。

鉴权、密钥与第三方服务

页面的登录 UI 使用 useAuth();Func 只负责保护后端动作。密钥通过 process.env.NAME 读取,由用户在 Talizen「Backend / Env」面板 panel/backend/env 配置。智能体不得声称已代管环境变量,也不得把密钥写入配置、代码、组件、示例、注释或生成结果。

export async function main(input, ctx) {
  ctx.auth.requireUser()
  const response = await fetch('https://api.example.com/v1/generate', {
    method: 'POST',
    headers: {
      Authorization: 'Bearer ' + process.env.EXAMPLE_API_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(input),
  })
  if (!response.ok) {
    ctx.response.status(502)
    throw new Error('Upstream request failed')
  }
  return response.json()
}

接收 Webhook 时,按供应商规范读取原始正文并使用 Web Crypto 校验签名,再解析 JSON;不要先重写正文。

登录与用户相关的服务端动作有单独的文档:在 Func 里实现登录找回与修改密码注册时验证邮箱在 Func 里查询用户

支付集成

Func 可对接支付宝等支付服务,但平台不内置支付 SDK。服务端必须生成订单、保管密钥、验证签名与商户身份,并以幂等方式处理异步通知。完整示例见使用 Func 接入支付宝电脑网站支付

浏览器调用与 SSR 边界

写入操作应从浏览器事件处理器调用 Func,持久化状态留在 Func/JSON 表,而不是只放 React state。公开 HTTP 路径使用 /func/<key>;不要在页面路由中占用 /func/*

不要在 getServerSideProps 中调用 Func。SSR 只提供适合首屏公共数据的请求/Cookie 辅助能力,刻意不暴露 ctx.authctx.funcctx.dbctx.cache。鉴权、私有数据、写入和缓存/数据库逻辑应留在 Func 与浏览器交互流程。

开发与验收流程

  1. 确认 CMS 或 talizen/form 不能满足需求。
  2. 创建或核对 /platform/table 下需要的 JSON 表
  3. /backend/func 编写 ESM 导出,验证输入并从 process.env 读取密钥。
  4. 页面使用 invoke('key.method', input);仅流式场景使用原生 Fetch/SSE。
  5. 使用 run_functalizen func run 传入样例做后端自测;记住测试超时只作用于本次运行。
  6. 修改页面或组件后运行 lint,并在真实页面验证成功、业务失败、未登录、第三方失败、超时和流式结束路径。
  • 没有项目/站点/内部表 ID 进入浏览器 payload。
  • 没有硬编码密钥、旧式全局变量、手动方法分发或计时器。
  • 表已存在,输入校验写在 Func 里。
  • 受保护操作调用 requireUser(),记录使用 user.id
  • 大文件走资源上传,第三方/Webhook/支付校验错误与签名。
  • 普通调用返回结构化 JSON;SSE 正确缓冲 frame,并处理 done/error
  • 调用端超时与实际任务时长匹配,生产路径已经验证。

Render diagnostics