# Build site backend workflows with Func | Talizen API for AI | Talizen

> A Talizen Func overview covering backend functions and keys, runtime rules, the ctx capability reference, results and errors, auth and secrets, payments and the SSR boundary, plus deep dives on JSON tables, asset uploads, timeouts, and streaming.

[Talizen API](/)

[EN](/api/func-backend.md)/ [中文](/api/func-backend.md?lang=zh)

### Overview

- [Talizen API for AI](/api.md)

### Discoverability

- [How to optimize llms.txt](/api/optimize-llms-txt.md)

### Site configuration

- [Configure talizen.config.ts](/api/talizen-config.md)
- [Implement domain-based locale routing](/api/domain-locale-routing.md)

### Backend

- [Calling external APIs on the server and managing the cache](/api/ssr-external-api-cache.md)
- [Build site backend workflows with Func](/api/func-backend.md)
- [JSON tables: definition, reads, and queries](/api/func-json-tables.md)
- [Uploads: signed direct upload and Func-generated files](/api/func-assets-upload.md)
- [Timeouts and streaming responses](/api/func-timeout-streaming.md)
- [Integrate Alipay PC Web Payment with Func](/api/func-alipay-payment.md)

### Auth

- [Require a Verified Email to Sign Up](/api/auth-verified-registration.md)
- [Reset and Change Passwords](/api/auth-password-reset.md)
- [Sign In From a Func](/api/auth-func-login.md)
- [Query users from a Func](/api/func-user-directory.md)

### Integrations

- [Send Email and Verification Codes with Integrations](/api/func-email-integration.md)
- [Take Alipay payments with an integration](/api/func-alipay-integration.md)

### Help center

[Talizen Help](/docs/start.md)

Product guides for building sites in the editor.

BackendBuild site backend workflows with Func

# Build site backend workflows with Func

The Func overview for AI coding agents: when to use it, files and keys, runtime rules, the whole ctx surface, results and errors, auth and secrets, the SSR boundary, and the acceptance workflow. Tables, uploads, timeouts, and streaming each have a dedicated page.

Func is the project-scoped backend runtime for Talizen / Talizen. Use it for small server-side workflows such as protected writes, bookings and waitlists, profile updates, third-party APIs, webhooks, payment integrations, and AI requests that need secrets or bounded streaming.

**Agent objective**

Keep server code in `/backend/func`, use platform capabilities through `ctx`, and call stable Func keys from pages. Never leak project IDs, secrets, identity logic, or persistent writes into browser code.

This page is the **overview**: when to use Func, files and methods, runtime rules, the whole `ctx` surface, results and errors, auth and secrets, the SSR boundary, and the acceptance workflow. Topics that need real depth — tables, uploads, timeouts and streaming — each have their own page, listed under [Deep dives](#deep-dives).

## When to use Func

### Good Func workloads

Bookings, waitlists, RSVP, lead routing, profile updates, availability checks, signed-in actions, third-party APIs, webhooks, payments, and simple JSON data reads/writes.

### Prefer built-ins

Use CMS for ordinary content, `talizen/form` for static contact forms, and `talizen/auth` for login UI and session state.

### Not a Func workload

Detached background jobs, heavy file processing, unbounded streams, timer polling, custom identity/session systems, OAuth callbacks, or token exchange.

### Project isolation

Func is already scoped to the current project. Inputs, source code, and branches must not contain `project_id`, `site_id`, or internal table IDs.

## Files, keys, and methods

Func files live in `/backend/func`. The extensionless file path is the Func key: `/backend/func/booking.ts` maps to `booking`, and `/backend/func/profile/settings.ts` maps to `profile/settings`.

Dots are reserved for exported methods. Export `main` for one operation, or export related operations directly from one file. Do not write a manual dispatcher.

```typescript
// /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 } })
}
```

```typescript
invoke('booking.create', input)       // key booking, method create
invoke('profile/settings.update', input)
invoke('booking', input)              // key booking, method main
```

## Runtime and code rules

- Use ESM exports: `export function method(input, ctx)`. Import only TypeScript types from `talizen/func-runtime` when needed.
- Access every platform capability through `ctx`. Do not use legacy globals such as `data`, `db`, `auth`, or `cache`. The sandbox has no module loader, so any **value import** fails.
- Validate, trim, and normalize all input inside the Func. Return structured JSON for expected business states; throw for invalid requests or unexpected failures.
- Func is not a full Node.js runtime. Do not depend on Node built-ins. `setTimeout`/ `setInterval` are unsupported and must not be used for delays, polling, or retries.
- Standard `fetch`, `Response`, `TextDecoder`, and Web Crypto are available for upstream HTTP, response reading, and signature verification.
- Symmetric encryption goes through Web Crypto on the global `crypto`: both `AES-GCM` and `AES-CBC` are supported, and `AES-CBC` applies compatible PKCS#7 padding automatically — see [the Alipay integration](/api/func-alipay-payment.md). Never import `node:crypto`.

## ctx capability reference

### Data

`ctx.db.get/query/insert/update/delete` operates on project JSON tables, returning `{ total, list, limit }` or a single record. [Query syntax and write rules →](/api/func-json-tables.md)

### Authentication

`ctx.auth.currentUser()` reads the current user; `ctx.auth.requireUser()` rejects unauthenticated requests.

### User directory

`ctx.users.find/query` resolves one user or pages through the list. Its scope is the **whole project**, not the caller. [Querying users and the gate you must write →](/api/func-user-directory.md)

### Cache

`ctx.cache.get/set/del/incr/expire` supports short-lived results, counters, and expiring state. It is not persistent storage.

### Request and response

`ctx.request.host/ip/method/path` exposes request metadata; raw bodies follow Fetch reading semantics. Use `ctx.response.status(code)` to set status.

### Cookies

Read, set, or delete cookies through `ctx.cookies`. After the first SSE event, headers are committed and cookies cannot change.

### Assets

`ctx.assets.upload({ filename, mimeType, base64 })` uploads files generated inside Func. [Choosing between the two upload paths →](/api/func-assets-upload.md)

### Email

`ctx.email.send/sendCode/verifyCode` requires a connected email integration; credentials never reach the sandbox. [Send email through an integration →](/api/func-email-integration.md)

### Streaming

`ctx.sse.send(event, data)` sends bounded SSE events. The platform supplies the final `done` or `error` event. [Timeouts and streaming →](/api/func-timeout-streaming.md)

### Diagnostics

Use `console.log/warn/error` and correlate one call with `ctx.trace_id`. Never log secrets or sensitive bodies.

## Deep dives

These topics carry enough implementation detail to warrant their own pages:

### [JSON tables: definition, reads, and queries →](/api/func-json-tables.md)

The definition file format and its validation, record shape, `where` and `filter` operators and their traps, paging and ordering, merge-style updates, and the boundary people miss: tables belong to the project, not to a site version.

### [Uploads: signed direct upload and Func-generated files →](/api/func-assets-upload.md)

Browser files go straight to the CDN, Func-generated bytes go through `ctx.assets.upload`, and why a base64 relay is never the answer.

### [Timeouts and streaming responses →](/api/func-timeout-streaming.md)

Where `timeoutMs` belongs, how to diagnose `context deadline exceeded` in order, and the complete native Fetch + SSE parsing loop.

### [Integrate Alipay PC web payment →](/api/func-alipay-payment.md)

A complete example: public-key mode, RSA2, optional AES content encryption, the order table, asynchronous notification verification, and idempotent updates.

## Results and errors

The HTTP layer returns `{ "result": ... }` or `{ "error": "..." }`. Browser `invoke()` unwraps successful results and throws `TalizenFuncError` on failure. Return explicit objects for expected states such as no availability or duplicate submission.

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

try {
  const booking = await invoke('booking.create', input)
} catch (error) {
  const message = error instanceof TalizenFuncError
    ? error.message
    : 'Unable to submit. Try again later.'
}
```

> The two directions are not symmetric: the browser receives a `TalizenFuncError` instance, but **inside Func** platform capabilities such as `ctx.db` throw **strings**, not `Error` objects. In a server-side `catch (e)`, `e.message` is `undefined` — use `String(e)`.

### Return a raw HTTP Response

Plain return values keep the JSON envelope. For webhooks and payment callbacks that require an exact acknowledgement, return the global `Response` provided by the Func runtime. It bypasses `{ result: ... }` and sends the selected status, headers, and body.

```typescript
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' },
  })
}
```

The `Response` constructor is global and needs no import. For explicit annotations, `talizen/func-runtime` exports type-only `Response` and `ResponseInit`. Call a raw-Response method with native `fetch()`, not JSON-unwrapping `invoke()`.

JSON, form, text, and binary bodies can reach Func. A non-JSON request receives `{}` as `input`; read the exact one-shot body through `ctx.request.text()` or `arrayBuffer()` and do not re-encode it before signature verification.

## Auth, secrets, and external services

Use `useAuth()` for login UI; use Func auth only to protect backend actions. Read secrets with `process.env.NAME`. Users configure them in the Talizen Backend / Env panel at `panel/backend/env`. Agents must not claim to manage platform env vars or place secrets in config, Func source, components, examples, comments, or generated output.

```typescript
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()
}
```

For webhooks, read the raw body as required by the provider and verify its signature with Web Crypto before parsing JSON.

Sign-in and user related server actions have their own pages: [Sign In From a Func](/api/auth-func-login.md), [Reset and Change Passwords](/api/auth-password-reset.md), [Require a Verified Email to Sign Up](/api/auth-verified-registration.md), and [Query users from a Func](/api/func-user-directory.md).

### Payment integrations

Func can integrate with providers such as Alipay, but the platform does not include a payment SDK. Server code must create orders, protect keys, verify signatures and merchant identity, and process asynchronous notifications idempotently. For a complete example see [Integrate Alipay PC web payment with Func](/api/func-alipay-payment.md).

## Browser calls and the SSR boundary

Call mutating Funcs from browser event handlers. Keep persistent state in Func/JSON tables rather than React state alone. The public HTTP path is `/func/<key>`; do not occupy `/func/*` with page routes.

Do not call Func from `getServerSideProps`. SSR exposes request/cookie helpers for public or cookie-vary-safe first-render data, but intentionally does not expose `ctx.auth`, `ctx.func`, `ctx.db`, or `ctx.cache`. Keep auth, private data, writes, and cache/database logic in Func/browser flows.

## Development and acceptance workflow

1. Confirm CMS or `talizen/form` is insufficient.
2. Create or verify the required [JSON tables](/api/func-json-tables.md) under `/platform/table`.
3. Write ESM exports in `/backend/func`, validate input, and read secrets from `process.env`.
4. Use `invoke('key.method', input)` from pages; use native Fetch/SSE only for streaming.
5. Run sample backend tests with `run_func` or `talizen func run`. Remember that the test timeout applies only to that run.
6. Run lint after page/component edits and test success, business failure, unauthenticated, upstream failure, timeout, and stream completion paths in the real page.

- No project/site/internal table IDs enter browser payloads.
- No hard-coded secrets, legacy globals, manual dispatchers, or timers.
- Tables exist and input validation lives in the Func.
- Protected actions call `requireUser()`; records use `user.id`.
- Large files use asset upload; third parties, webhooks, and payments validate errors and signatures.
- Ordinary calls return structured JSON; SSE buffers frames and handles `done`/ `error`.
- Caller timeout matches workload duration and the production path is verified.

**Completion criteria**

A correct Func has a stable key, minimum necessary access, validated input, predictable results, controlled persistence, and error/timeout behavior reproducible through the real page call path.

[Previous\
\
Calling external APIs on the server and managing the cache](/api/ssr-external-api-cache.md) [Next\
\
JSON tables: definition, reads, and queries](/api/func-json-tables.md)

On this page

- [When to use Func](#when-to-use)
- [Good Func workloads](#good-func-workloads)
- [Prefer built-ins](#prefer-built-ins)
- [Not a Func workload](#not-a-func-workload)
- [Project isolation](#project-isolation)
- [Files, keys, and methods](#files-and-methods)
- [Runtime and code rules](#runtime-rules)
- [ctx capability reference](#context)
- [Data](#data)
- [Authentication](#authentication)
- [User directory](#user-directory)
- [Cache](#cache)
- [Request and response](#request-and-response)
- [Cookies](#cookies)
- [Assets](#assets)
- [Email](#email)
- [Streaming](#streaming)
- [Diagnostics](#diagnostics)
- [Deep dives](#deep-dives)
- [JSON tables: definition, reads, and queries →](#json-tables-definition-reads-and-queries)
- [Uploads: signed direct upload and Func-generated files →](#uploads-signed-direct-upload-and-func-generated-files)
- [Timeouts and streaming responses →](#timeouts-and-streaming-responses)
- [Integrate Alipay PC web payment →](#integrate-alipay-pc-web-payment)
- [Results and errors](#responses)
- [Return a raw HTTP Response](#raw-response)
- [Auth, secrets, and external services](#auth-secrets-external)
- [Payment integrations](#payments)
- [Browser calls and the SSR boundary](#browser-ssr)
- [Development and acceptance workflow](#workflow)

Looking for the help center?

[Talizen Help](/docs/start.md)

> 全站页面清单：[/llms.txt](/llms.txt)
