@everystack/api

中文文档
0.4.17发布于 2026/11/13AGPL-3.0-onlyTypeScriptCommonJS零依赖近 30 天下载 2273

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

中文使用文档
文档目录18 节
  1. 安装
  2. 条目点
  3. 处理器
  4. 挂载为 Expo 路由器 API 路由
  5. 处理器选项
  6. 行级安全( pgSettings)
  7. 角色接入控制
  8. 拒绝代码
  9. 命名公约
  10. 查询协议
  11. 客户端
  12. 查询数据
  13. 变异
  14. 过滤方法
  15. 调试刷新
  16. 导出类型
  17. 同伴依赖关系
  18. 许可证

@ verystack/ api (英语)

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

安装

pnpm add @everystack/api drizzle-orm

条目点

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

处理器

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

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

const handler = createHandler(db, schema);

挂载为 Expo 路由器 API 路由

// 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); }

处理器选项

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 状态 messageJSON机构增加了一个 code数字 :

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

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

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

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

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 运行时间工作 .

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 接口,不导入认证软件包.

查询数据

// 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();

变异

// 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();

过滤方法

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

// 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 在部署发送代码的服务器后。 此选项在下次断开时消失 . 见 客户指南 财务报告和审定财务报表 届会指南。 。 。 .

导出类型

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

部分 无所事事 ——AWS上的"博览会"应用程序自发托管的应用程序堆栈.

许可证

仅限AGPL-3.0 • 可扩展技术公司.

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

版本与发布记录

@everystack/api 共发布 32 个版本,首次发布于 2026/05/15;过去 12 个月发布了 32 个版本。

版本发布日期距今
0.4.17latest2026/11/131 天前
0.4.162026/11/122 天前
0.4.152026/11/068 天前
0.4.142026/11/059 天前
0.4.132026/10/3114 天前
0.4.122026/10/2520 天前

关于 @everystack/api 的常见问题

@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 和提交记录判断。