尼罗河Js
一个功能性的TypeScript后端,用于小团队发布不断增长的产品。
您编写操作,将它们分组到服务中,然后Nile Js为您提供了一个具有验证、错误处理和模式导出功能的可预测API。不需要维护路由树、控制器或中间件链:每个业务操作都在一个众所周知的地方,因此小团队可以随着后端业务的增长而不断发布产品。
升级
读了 迁移笔记 在升级现有应用程序之前。
安装
从CLI启动(推荐)
bunx @nilejs/cli new my-app
cd my-app
bun install
bun run dev
启动器运行在Bun的原生HTTP服务器上,带有一个嵌入式PGlite数据库 tasks 用于新服务和操作的服务和生成器。看到 @nilejs/cli.
添加到现有项目中
bun add @nilejs/nile zod
结果、选择和 safeTry 来自 @nilejs/nile/fp,再出口 slang-ts.
对于数据库帮助程序(createModel, getZodSchema),也加入Drizzle:
bun add drizzle-orm drizzle-zod
对于前端,使用 @nilejs/client.
快速启动
1. 写一个动作
动作是一个命名的模式、从中推断的输入类型和一个返回的处理程序 Ok 或者 Err.
// src/services/tasks/actions/create.ts
import { createAction } from "@nilejs/nile";
import { Ok } from "@nilejs/nile/fp";
import { z } from "zod";
const createTaskSchema = z.object({
title: z.string().min(1, "Title is required"),
});
type CreateTaskInput = z.infer<typeof createTaskSchema>;
const createTaskHandler = (input: CreateTaskInput) => {
const task = {
id: crypto.randomUUID(),
title: input.title,
status: "pending",
};
return Ok({ task: task });
};
export const createTaskAction = createAction<CreateTaskInput>({
name: "create",
description: "Create a task",
validation: createTaskSchema,
handler: createTaskHandler,
});
2. 将操作分组到一个服务中
// src/services/tasks/index.ts
import { createService } from "@nilejs/nile";
import { createTaskAction } from "./actions/create";
export const tasksService = createService({
name: "tasks",
description: "Task management",
actions: [createTaskAction],
});
// src/services/services.config.ts
import { createServices } from "@nilejs/nile";
import { tasksService } from "./tasks";
export const services = createServices([tasksService]);
3. 启动服务器
// src/index.ts
import { createNileServer } from "@nilejs/nile";
import { services } from "./services/services.config";
const server = await createNileServer({
serverName: "my-app",
runtime: "bun",
native: true,
services: services,
rest: {
baseUrl: "/api",
port: 8000,
allowedOrigins: ["http://localhost:3000"],
},
});
if (server.nativeRest) {
Bun.serve({ port: 8000, fetch: server.nativeRest.fetch });
}
遗漏的应用程序 native 在Bun或Node.js上使用Hono适配器 server.rest.app. 看到 运行时适配器.
4. 叫它
curl -X POST http://localhost:8000/api/services \
-H "Content-Type: application/json" \
-d '{"intent":"execute","service":"tasks","action":"create","payload":{"title":"Ship it"}}'
{
"status": true,
"message": "Action completed.",
"data": {
"task": { "id": "a1b2c3d4-...", "title": "Ship it", "status": "pending" }
}
}
从前端来看, @nilejs/client 返回与处理程序相同的Result形状:
import { createNileClient } from "@nilejs/client";
const nile = createNileClient({ baseUrl: "http://localhost:8000/api" });
const result = await nile.invoke({
service: "tasks",
action: "create",
payload: { title: "Ship it" },
});
if (result.isErr) {
console.error(result.error);
} else {
console.log(result.value);
}
动作输入和响应
HTTP操作调用使用 POST 来 {baseUrl}/services 同一个身体:
{
intent: "explore" | "execute" | "schema",
service: string, // a service name, or "*" for all
action: string, // an action name, or "*" for all
payload: object // the action's input; use {} when there is none
}
| 意图 | 它是做什么的 |
|---|---|
execute | 根据操作的模式验证有效负载并运行操作。 |
explore | 列出服务或一个服务中的操作。 |
schema | 为客户端、工具和AI代理返回一个动作的输入作为JSON Schema。 |
每个HTTP动作响应都有这三个字段:
{
status: boolean, // true on success
message: string, // a readable summary
data: object // the action's value, validation details, or {}
}
验证失败使用Zod的美化消息,并包含一个生成的惟一 data.errorCode 与 data.issues:
{
"status": false,
"message": "✖ Title is required\n → at title",
"data": {
"errorCode": "jN8sQ4tV2pLm",
"issues": [{ "path": "title", "message": "Title is required" }]
}
}
上面的代码是说明性的;每个失败都有一个新的引用。意外异常在内部记录,并返回安全消息,例如 Error Code: jN8sQ4tV2pLm - could not complete the action. 诊断代码与日志匹配 log_id. 自定义成功消息和有意的 Err 信息保持其措辞。
属性命名 raw 递归地从出站JSON和Socket中删除。IO消息,包括自定义JSON响应。将原始原因保留在后端,并为公共数据选择不同的字段名称。HTTP JSON响应被缓冲以应用此过滤。
发现(explore 和 schema)是关闭的,除非您使用 rest.discovery,并且需要保密。看到 API的发现.
项目结构
快速启动产生如下布局:
src/
index.ts
services/
services.config.ts
tasks/
index.ts
actions/
create.ts
Nile在引导时检查源代码结构,默认情况下,如果违反就会失败。每个服务都需要一个 index.ts 和一个 actions/ 目录中。当一个动作发展时,把它移动到 actions/create/create.ts,从 actions/create/index.ts,并安排私人助手 actions/create/modules/. 超过400行的操作入口点将被报告用于重构。
集 project: { root: ".", structure: "warn" } 在服务器配置中报告违规并继续,或设置 structure: false 禁用检查。在一个单一的,点 root 在后端包中。无源包跳过检查,除非根是显式的。看到 应用程序结构.
结果和实用程序
导入结果帮助程序和俚语的实用程序 @nilejs/nile/fp;此版本包含俚语0.2.2。直接返回结果,而不是嵌套它们: Ok(Ok(value)) 和 Ok(Err(message)) 被拒绝了。
使用 safeTry 对于数据库操作和其他可能抛出的承诺。它保留了原来的原因 result.raw 用于内部诊断。尼罗河的 dbTry 助手已被移除。使用 utils.describeError 用于有界诊断元数据和 utils.handleError 以获取安全的来电信息。尼罗河帮手,包括 createModel 和 getZodSchema,均可透过 utils 名称空间:
import { utils } from "@nilejs/nile";
import { safeTry } from "@nilejs/nile/fp";
看到 共享公用事业 为例子。
钩子
每个动作都运行一个全局before钩子。将其用于共享策略,例如角色检查:
import { type BeforeActionHandler, createNileServer } from "@nilejs/nile";
import { Err, Ok, type Result } from "@nilejs/nile/fp";
import { services } from "./services/services.config";
const requireSessionHook: BeforeActionHandler<boolean, string> = (
hookInput
): Result<boolean, string> => {
if (!hookInput.action.isProtected) {
return Ok(true);
}
const session = hookInput.nileContext.getSession("rest");
if (session === undefined) {
return Err("Sign in to continue");
}
return Ok(true);
};
const server = await createNileServer({
serverName: "my-app",
services: services,
onBeforeActionHandler: requireSessionHook,
});
action也可以在本地列出 hooks.before 和 hooks.after,引用其他已注册的操作。对于已执行的操作,顺序是:
JWT check (protected actions)
-> global before hook
-> local before hooks
-> validation
-> handler
-> local after hooks
-> global after hook
-> response
任何失败的步骤都将停止其余步骤,除了标记为 isCritical: false. 看到 挂钩和管道.
还包括什么?
| 特征 | 指南 |
|---|---|
| JWT身份验证和访问策略 | 身份验证和挂钩 |
通过HTTP调用,从其他操作调用,从测试调用 server.invoke | 调用操作 |
| 带有限制和类型检查的多部分文件上传 | 文件上传 |
| 套接字。IO操作和键入主题 | 尚 |
| 每个操作或每个客户端的速率限制 | 速度限制 |
| Cron时间表 | Cron作业 |
| 排队、脱衣、节流 | 排队、脱衣、节流 |
| 重试和共享实用程序 | 重试和实用程序 |
| 带有验证和分页的毛毛雨模型 | 数据库模型 |
| 结构化日志记录和共享资源 | 上下文和日志 |
| 准备就绪检查和正常关机 | 准备就绪和关机 |
| 遥测 | 遥测 |
启动数据
在启动时,Nile Js打印一个包含操作计数和注册时间的服务表。集 logServices: false 在 createNileServer 关掉它。代码中也有相同的数字:
const stats = server.stats();
console.table(stats.services);
// { totalServices, totalActions, registrationDurationMs, startupDurationMs,
// services: [{ name, description, actionCount, registrationDurationMs }] }
所有持续时间都以毫秒为单位。 startupDurationMs 涵盖服务器初始化,包括 onBoot 和REST设置,但不包括模块导入。每次调用都会返回一个新的副本,并且在启动后数字不会改变。
模式转换
Nile Js仅在发现请求首次需要时才将操作的Zod模式转换为JSON模式,然后缓存结果。启动和普通执行永远不会转换模式;验证总是贯穿Zod。
缓存是由模式对象键控的,因此以后对模式描述或默认值的更改不会被拾取。要在每次发现请求时重新转换,请设置 cacheSchema: false 在一个行动或 rest.discovery.cacheSchemas: false 用于整个REST应用程序。
钩子执行上下文
在处理程序或钩子中, context.hookContext 保存该执行的状态、输出、错误和日志。嵌套操作调用获得自己的上下文,并在完成时恢复父操作调用的上下文。在执行死刑之外, context.hookContext 是回退状态,而不是最后一个请求的状态。要在调用后检查钩子日志,请设置 result: { pipeline: true } 关于动作和阅读的返回 pipeline 字段。
与编码助手一起使用
包裹运送 SKILL.md,是与已安装版本匹配的编码助手指南。后 bun install 它在。 node_modules/@nilejs/nile/SKILL.md. 看到 尼罗河的技艺 的设置。
要求
- 本机传输和CLI启动器的Bun 1.4.2或更高版本。
- Node.js 22.23.4+, 24.22.1+或26.12.1+,当你在Node.js上运行Hono适配器时。
- TypeScript 7(一个对等依赖)。zod4和slang-ts随包一起安装。在您自己的依赖项中列出zold,并从
@nilejs/nile/fp所以你的代码使用了尼罗的副本。
贡献
尼罗河Js最初是由Hussein Kizz建造的 尼罗河小队实验室 为自己的B2B产品提供动力,现在是开源的。读 CONTRIBUTING.md 开始,并加入 社区 问问题。
许可证
麻省理工学院