# @everystack/api 中文文档

规范地址：https://npmcn.com/package/%40everystack/api/

版本：0\.4\.17
npm 元数据更新时间：2026-11-13T01:38:22.753Z
中文译文更新时间：2026-11-14T06:21:08.290Z
软件包许可：AGPL-3\.0-only

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

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

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

- 包体积（解压后）：285 kB，14 个文件
- 模块格式：CommonJS
- TypeScript 类型：包内自带
- 运行依赖：0 个
- peer 依赖：drizzle-orm 0\.41\.0
- 首次发布：2026-05-15T04:42:33.950Z
- 最新版本发布：2026-11-13T01:38:22.547Z
- 近 30 天下载量：2273（2026-10-14 至 2026-11-12，npm 官方下载统计）
- 已发布版本数：32（近 12 个月 32 个）

## 中文简介

PostgREST 兼容的 HTTP 处理器 + 为 Drizzle ORM 获取客户端

## 安装

```sh
npm install @everystack/api
```

也可以使用：`pnpm add @everystack/api`、`yarn add @everystack/api`、`bun add @everystack/api`

## README

# @ verystack/ api (英语)

PostgREST相容的HTTP处理器并打入Drizzle ORM取取客户端.

## 安装

```bash
pnpm add @everystack/api drizzle-orm
```

## 条目点

| 导入 | 说明 |
| --- | --- |
| `@everystack/api/handler` | 服务器侧 PostgREST 处理器 |
| `@everystack/api/client` | 客户端键入取取客户端 |

## 处理器

创建网络标准 `(Request) => Promise<Response>` 处理器来自 Drizzle 数据库实例和计划。 与Expo Router API线路, Cloudflare Workers, Bun, Deno 或任何支持请求/响应接口的平台合作.

```typescript
import { createHandler } from '@everystack/api/handler';
import { db } from './db';
import * as schema from './db/schema';

const handler = createHandler(db, schema);
```

### 挂载为 Expo 路由器 API 路由

```typescript
// app/api/[...path]+api.ts
import { createHandler } from '@everystack/api/handler';
import { db } from '../../db';
import * as schema from '../../db/schema';

const handler = createHandler(db, schema, { basePath: '/api' });

export function GET(request: Request) { return handler(request); }
export function POST(request: Request) { return handler(request); }
export function PATCH(request: Request) { return handler(request); }
export function DELETE(request: Request) { return handler(request); }
```

### 处理器选项

```typescript
createHandler(db, schema, {
  basePath: '/api',

  auth: {
    verifyToken: async (token) => verifyJWT(token),
    publicRoutes: ['GET'],
    publicRpc: ['health', 'search'],
    onAuthenticated: (user) => {},

    // Role-based access control
    roles: {
      posts: { read: 'public', write: 'authenticated' },
      admin_logs: { read: 'admin', write: 'admin' },
      '*': { read: 'authenticated', write: 'authenticated' },
    },
    roleHierarchy: ['public', 'authenticated', 'moderator', 'admin'],
    roleField: 'role',

    // Client credentials (two-tier auth)
    verifyClient: async (clientId, clientSecret) => lookupClient(clientId, clientSecret),
    clientHeaders: { id: 'X-Client-Id', secret: 'X-Client-Secret' },
    onClientAuthenticated: (client) => {},
  },

  rpc: {
    async timeline(body, user, client) {
      return db.query.posts.findMany({ limit: 20 });
    },
  },

  relations: {
    posts: {
      author: { table: 'users', from: 'authorId', to: 'id' },
      comments: { table: 'comments', from: 'id', to: 'postId', many: true },
    },
  },

  // lifecycle: true tables, the one soft delete. deriveHandlerConfig(models) emits this list;
  // do not write it by hand. Restore is POST {basePath}/{table}/restore, client .restore(id).
  lifecycle: ['posts', 'comments'],

  pgSettings: (user, client) => ({
    'request.jwt.claims': JSON.stringify(user || { role: 'anon' }),
    'app.user_id': String(user?.sub || ''),
  }),

  hooks: {
    posts: {
      beforeCreate: async (body, user, client) => ({ ...body, authorId: user?.sub }),
      afterCreate: async (row, user, client) => { /* notify */ },
      beforeUpdate: async (body, user, client) => body,
      afterUpdate: async (rows, user, client) => {},
      // All models; veto from '@everystack/api/handler' rolls back the request:
      beforeDelete: (row, ctx) => (ctx.user ? undefined : veto('sign in to delete')), // in the transaction; veto -> 409
      afterDelete: async (rows, ctx) => {},  // after commit, once per request; ctx.batch is null
      afterRestore: async (rows, ctx) => {}, // after commit, ctx.batch and ctx.counts
    },
  },

  naming: 'snake_case', // or 'camelCase' (default)
});
```

### 行级安全( pgSettings)

何时 `pgSettings` 提供,每个 CRUD 查询都被包裹在 PostgreSQL 交易中 `set_config()` 打电话 这将注入 RLS 政策可以通过 `current_setting()`。这些值是交易的LOCAL——它们从不通过集合连接泄露。 未提供时, 查询会直接执行而不进行包装 .

### 角色接入控制

何时 `auth.roles` 配置,每个表格路由对照HTTP方法所需的角色检查用户的角色. 决议: `roles[tableName]` → `roles['*']` → `null` (倒计时) `publicRoutes`) (中文(简体) ). 特殊角色: `'public'` (不需要任何标志), `'authenticated'` (任何有效的JWT). 出错代码:401 (无/无效令牌),403(作用不足).

### 拒绝代码

认证拒绝保留其现有的 HTTP 状态 `message`JSON机构增加了一个 `code`数字 :

| 代码 | 含义 |
| --- | --- |
| `token_refused` | 出示的出入证被拒绝. |
| `refresh_refused` | 拒绝更新令牌,包括重新使用或无法使用. |
| `auth_required` | 没有提交所需的访问令牌. |
| `client_refused` | 客户证书缺失或被拒绝. |

拒绝确认和密码的代号没有拒绝代码. 错误的密码,账户暂停,验证错误,以及服务中断没有拒绝代码. 没有公认代码的401不能表示百事可拒的会话.

这个 `code` 键还包含其他错误值,包括数据库错误上的 PostgreSQL SQLSTATEs 和 `vetoed` 在钩上否决。 客户端必须精确匹配四个拒绝值中的一个, 绝不只是检查是否 `code` 已存在.

自定义认证服务器可以使用相同的响应帮助 :

```typescript
import { authRefusal, AUTH_REFUSAL_CODES, type AuthRefusalCode } from '@everystack/api/handler';

return authRefusal('token_refused', 'Invalid or expired token'); // status defaults to 401
```

`authRefusal(code, message, status?)` 准确返回 `{ message, code }` 与 `Content-Type: application/json`它没有运行时间依赖. `AuthRefusalCode` 是四个条目的结合 `AUTH_REFUSAL_CODES`。 。 。 .

### 命名公约

这个 `naming` 选项控制密钥在 API 边界的转换 :

-   `'snake_case'`:接受蛇 病例入出,返回蛇 病例出出出出出. POST/PATCH 天体映射出蛇 病例 为德瑞兹尔而取出骆驼Case.
-   `'camelCase'` 或省略:将 Drizzle 属性名称作为-is.

列分辨率为双向 : `?author_id=eq.5` 决定: `authorId` 自动,即使没有 `naming` 选项.

### 查询协议

处理器执行 PostgREST 查询协议 :

**过滤器** (单位:千美元)`?column=operator.value`:

| 运算符 | 说明 | 示例 |
| --- | --- | --- |
| `eq` | 等数 | `?status=eq.published` |
| `neq` | 不等于一个 | `?role=neq.admin` |
| `gt` 编号 : `gte` | 超过 | `?age=gt.18` |
| `lt` 编号 : `lte` | 少于 | `?price=lt.100` |
| `like` | 模式匹配 | `?name=like.*smith*` |
| `ilike` | 不区分大小写模式 | `?email=ilike.*@gmail.com` |
| `in` | 设定 | `?id=in.(1,2,3)` |
| `is` | 无效检查 | `?deletedAt=is.null` |

**否认** (单位:千美元)`not.` 前缀 : `?status=not.eq.draft`, (中文(简体) ), `?tags=not.in.(a,b)`, (中文(简体) ), `?deletedAt=not.is.null`

**逻辑组**数字 :

-   `?or=(status.eq.draft,status.eq.pending)` - 其他资源条件
-   `?and=(price.gte.10,price.lte.100)` - 明确和分组
-   与正则过滤器组合 : `?category=eq.electronics&or=(status.eq.draft,status.eq.pending)`

**合计数** (邮政 `column.function()` 语法 :

-   `?select=count()` ——数起所有行
-   `?select=platform,total:amount.sum()` - 以别名组成的集团
-   获得支持: `count()`, (中文(简体) ), `sum()`, (中文(简体) ), `avg()`, (中文(简体) ), `min()`, (中文(简体) ), `max()`

**JSON 路径过滤**数字 : `?metadata->>theme=eq.dark`, (中文(简体) ), `?data->settings->>mode=eq.compact`

**列选择**数字 : `?select=id,title,author(name,email)`

**嵌入关系**数字 : `?select=*,author(*),comments(body)`

**订购**数字 : `?order=createdAt.desc`

**倾斜**数字 : `?limit=10&offset=20`

**精确计数**: 发送 `Prefer: count=exact` 要获取的页眉 `Content-Range: 0-9/42`

**变异**数字 :

-   `POST /table` - 插入(返回创建的行,201)
-   `PATCH /table?id=eq.1` — 更新匹配行 (200)
-   `DELETE /table?id=eq.1` — 删除匹配行(需要过滤器, 200)

**难民保护委员会**数字 : `POST /rpc/function_name` 与JSON体

## 客户端

纯取零反应依赖的客户端 。 在任何 JavaScript 运行时间工作 .

```typescript
import { createClient } from '@everystack/api/client';
import { session } from '@everystack/auth/api';

const api = createClient({
  baseUrl: '/api',
  session: session.forClient(),
  headers: { 'X-Custom': 'value' },
});
```

`session` 优先于被消耗的 `getToken`, (中文(简体) ), `onTokenExpired` 财务报告和审定财务报表 `onAuthFailure` 召回电话. 使用 `createSession` 从 `@everystack/auth/session` 用于自定义存储或认证. API 软件包接受结构 `ClientSession` 接口,不导入认证软件包.

### 查询数据

```typescript
// List with filters
const { data, error } = await api
  .from('posts')
  .eq('status', 'published')
  .order('createdAt', 'desc')
  .limit(10)
  .execute();

// Single record
const { data: post } = await api
  .from('posts')
  .eq('id', '123')
  .single();

// With exact count
const { data: posts, total } = await api
  .from('posts')
  .limit(20)
  .executeWithCount();

// Column selection + relation embedding
const { data } = await api
  .from('posts')
  .select('id,title,author(name)')
  .execute();

// Negation
const { data } = await api
  .from('posts')
  .notEq('status', 'draft')
  .notIn('category', ['spam', 'archived'])
  .execute();

// Logical groups
const { data } = await api
  .from('posts')
  .eq('active', 'true')
  .or('status.eq.draft,status.eq.pending')
  .execute();

// Aggregates
const { data } = await api
  .from('transactions')
  .select('platform,total:amount.sum()')
  .execute();
```

### 变异

```typescript
// Insert
const { data } = await api.from('posts').insert({
  title: 'Hello',
  content: 'World',
});

// Update
const { data } = await api
  .from('posts')
  .eq('id', '123')
  .update({ title: 'Updated' });

// Delete
const { data } = await api
  .from('posts')
  .eq('id', '123')
  .remove();
```

### 过滤方法

所有过滤方法都返回构建器进行链化:

```typescript
// Standard operators
.eq(column, value)      // Equals
.neq(column, value)     // Not equals
.gt(column, value)      // Greater than
.gte(column, value)     // Greater or equal
.lt(column, value)      // Less than
.lte(column, value)     // Less or equal
.like(column, value)    // LIKE pattern
.ilike(column, value)   // Case-insensitive LIKE
.is(column, value)      // IS NULL check
.in(column, values[])   // IN array

// Negation operators
.notEq(column, value)   // NOT equal
.notNeq(column, value)  // NOT not-equal
.notGt(column, value)   // NOT greater than
.notGte(column, value)  // NOT greater or equal
.notLt(column, value)   // NOT less than
.notLte(column, value)  // NOT less or equal
.notLike(column, value) // NOT LIKE
.notIlike(column, value)// NOT ILIKE
.notIs(column, value)   // IS NOT NULL
.notIn(column, values[])// NOT IN array

// Logical groups
.or(expression)         // OR group: .or('status.eq.draft,status.eq.pending')
.and(expression)        // AND group: .and('price.gte.10,price.lte.100')
```

### 调试刷新

会议于401年更新一次 `token_refused`,然后客户端重复一次. 同步请求刷新 。 拒绝刷新或重试 `token_refused` 结束会话. `auth_required`, (中文(简体) ), `client_refused` 外国401从不启动刷新或签署. 一个失败的刷新保存会话, 返回有代码的状态 503 `refresh_unavailable`。 。 。 . `session.forClient({ whenRefreshFails: 'anonymous' })` 换句话说,没有无记名标志就再试一次. 签名 出局总是会赢得晚点喝的.

`legacyRefusals` 默认为 `true`: 准确的旧的百事可答留言仍然有效 `code` 缺席. 准备吧 `false` 在部署发送代码的服务器后。 此选项在下次断开时消失 . 见 [客户指南](https://npmcn.com/package/docs/client.md#authentication) 财务报告和审定财务报表 [届会指南](https://npmcn.com/package/docs/auth.md#sessions-on-the-client)。 。 。 .

## 导出类型

```typescript
import type {
  AuthConfig, RoleConfig, RelationConfig, SoftDeleteConfig,
  MutationEvent, HookConfig, HandlerOptions,
} from '@everystack/api/handler';

import type {
  ClientOptions, ClientError, ClientResponse,
  CountResponse, QueryBuilder, Client,
} from '@everystack/api/client';
```

## 同伴依赖关系

| 软件包 | 版本 |
| --- | --- |
| `drizzle-orm` | `>=0.35.0` |

* * *

部分 [无所事事](https://github.com/scalable-technology/everystack) ——AWS上的"博览会"应用程序自发托管的应用程序堆栈.

## 许可证

[仅限AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.html) • 可扩展技术公司.

向不能或不希望获得商业许可证的组织发放商业许可证 遵守AGPL-3.0条款。 关于商业许可证,联系 [许可证发放@可扩展性.技术](mailto:licensing@scalable.technology)。 。 。 .

## 常见问题

### @everystack/api 是什么？

PostgREST 兼容的 HTTP 处理器 \+ 为 Drizzle ORM 获取客户端

### 如何安装 @everystack/api？

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

### @everystack/api 支持 TypeScript 吗？

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

### @everystack/api 支持 ESM 还是 CommonJS？

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

### @everystack/api 可以商用吗？

@everystack/api 使用 AGPL-3\.0-only 许可。最严格的 Copyleft：除分发外，通过网络向用户提供基于它的服务也需要开源对应源码。闭源 SaaS 使用前务必评估。以上为概括，不构成法律意见，请以许可原文为准。

### @everystack/api 的下载量有多少？

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

### @everystack/api 还在维护吗？

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

