@aventara/nest
主办 Aventara AvFramework 在NestJS应用程序中。 它挂载框架的 HTTP 协议
(单位:千美元)AvProtocol.bind(framework) 从 @aventara/core/protocol在巢穴的HTTP平台上,并设置 AvFramework 输入
巢穴的依赖性注射。 协议的每一项规则(程序、解码、状态、信封、信头、缺位)都是
core's;此软件包仅将 Nest 请求翻译为协议请求并写回协议的答案.
它运行在巢穴12(@nestjs/common 财务报告和审定财务报表 @nestjs/core ^12.0.0),在快取(Nest的默认)或快取.
@aventara/core 这是它唯一的依赖.
骑着它
AventaraModule 是一个全局模块,在根模块中导入一次。 这需要 AvFramework 以四种形式之一.
// src/app.module.ts — the shape `aventara init` writes (`@aventara/cli`)
import { Module } from "@nestjs/common";
import { AventaraModule } from "@aventara/nest";
import { aventaraConfig } from "./aventara.config.js";
import { PrismaModule, PrismaService } from "./prisma.service.js";
@Module({
imports: [
PrismaModule,
AventaraModule.forRootAsync({
imports: [PrismaModule],
inject: [PrismaService],
useFactory: async (prisma: PrismaService) => ({
config: await aventaraConfig(prisma),
}),
}),
],
})
export class AppModule {}
// src/aventara.config.ts — the framework's configuration: api, exposure, pipelines, resources
import type { AvDiagnostic } from "@aventara/core";
import { createPrismaAdapter } from "@aventara/prisma7-adapter";
import { Logger } from "@nestjs/common";
import { discovery } from "./generated/aventara/discovery.artifact.js";
import type { PrismaService } from "./prisma.service.js";
const logger = new Logger("Aventara");
export async function aventaraConfig(prisma: PrismaService) {
return {
api: "/api",
exposed: true,
// The raw error of a server failure, with its stack, in Nest's log.
diagnostics: (diagnostic: AvDiagnostic) => {
const { error } = diagnostic;
logger.error(
`${diagnostic.code} (request ${diagnostic.requestId}): raw error`,
(error instanceof Error && error.stack) || String(error),
);
},
adapter: await createPrismaAdapter({
client: prisma,
discovery,
provider: "postgresql",
driver: "@prisma/adapter-pg",
}),
} as const;
}
| 表单 | 何时 |
|---|---|
AventaraModule.forRoot({ framework }) | 你打电话来 createFramework(config) 想要自己输入 AvFramework<M, Cfg> 在代码. |
AventaraModule.forRoot({ config }) | 模块调用 createFramework(config) 给你的. |
AventaraModule.forRootAsync({ imports, inject, useFactory }) 解决 { framework } 或者说 { config } | 构建框架的内容(一个Prisma客户端,一个配置服务)本身就是一个提供者. |
每种形式也采用 state,使每个协议要求的种子 ctx.$state 从平台请求(见
请求状态) (中文(简体) ).
这个 config 窗体的打法完全一样 createFramework'参数:注解 AvConfig<M>中线
字面,一个推断常数和一个 satisfies 检查被接受, 并检查您的适配器的模型.
@InjectFramework() (或 @Inject(AVENTARA_FRAMEWORK)输入 AvFramework。 DI 没有类型参数, 所以类型
带有自己别名的参数:
export type AppFramework = Awaited<ReturnType<typeof buildFramework>>;
@Injectable()
export class ReportsService {
constructor(@InjectFramework() private readonly framework: AppFramework) {}
}
在Nest建立应用程序之前,这些拒绝: framework 财务报告和审定财务报表 config页:1
framework 值不是一个框架; a state 选项; 模块在一个应用程序中挂载了两次; Express以外的平台
或快速化(或无,如 createApplicationContext(a) 减少 AvConstructionError,你的或模块的自己的
宣传内容不变。 巢穴的默认 abortOnError: true 退出进程为靴子陷阱出错,而不是拒绝;
通过 abortOnError: false 改为 NestFactory.create 来抓住它.
Nest的测试模块工作. Test.createTestingModule({ imports: [AppModule] }).compile() 建立提供者之前
任意 HTTP 平台存在; 模块等待一个 createNestApplication() 供应,仍然拒绝
在应用程序监听前不支持的平台. 一个完全没有应用的测试模块是
拒绝时 init(),作为一个平台 none。 。 。 .
模块没有资源,因此没有关闭的可能. 您的 Prisma 提供者关闭它自己的客户端
onModuleDestroy,与 app.enableShutdownHooks() 对于信号.
用手召唤它
逆着形状 aventara init 写入( api 挂载路径 /api,一个Prisma User 模式 :
# 1. The contract. Its protocol.hash is what every resource request sends as Aventara-Contract-Hash.
HASH=$(curl -s http://localhost:3000/api/_contract | node -p 'JSON.parse(require("fs").readFileSync(0, "utf8")).protocol.hash')
# 2. find.many on User, with both identity headers.
curl -s -X POST http://localhost:3000/api/_resources/User/find/many \
-H 'Content-Type: application/json' \
-H 'Aventara-Protocol-Version: 1' \
-H "Aventara-Contract-Hash: $HASH" \
-d '{}'
# {"data":[],"code":"A1000","cause":null}
留下一个身份信头 答案是 400 A2000,命名缺失的页眉。 散列变化
当方案或配置完成时:再次获取,并重新生成客户端.
资源键是模型名称,如所书. model User 实值 User 到处都是,在电线上
(单位:千美元)/_resources/User/find/many)和生成的客户端(avClient.User.find.many({})) (中文(简体) ). 没有东西被重新命名,
小写或复数化.
结果只读。 列表结果为 readonly 数组, 因此键入 readonly User[]数字 :
const users: readonly User[] = await avClient.User.find.many({});。 。 。 .
在协议路线上运行的是什么
协议的路线是平台路线 从模块注册 configure()。每个
protocol.surface() (每个广告业务, GET <api>/_contract,以及 POST <api>/_transactions 只有在客户合同发布广告时 interactive),加上:
其余部分 <api>/_*- 巢穴的吗 RouterExplorer 不列出平台路线,所以模块记录其一行
穿过巢穴 Logger (内容 AventaraModule· ; logger: false 像其他一样沉默不语:
[Nest] LOG [AventaraModule] Aventara mounted 10 operations at /api (+ GET /api/_contract, POST /api/_transactions, POST /api/_all)
内部失败( A)A3000– 说吧 –A3004,作为500)被记录为通过 Nest 的一条错误行 Logger
(内容 Aventara:代码,操作,出处和请求的ID,被抛出值的类名. 这个
永不日志消息,它可以持有 SQL 或连接字符串 。 原始错误仍然发生在您身上 diagnostics 下沉时
配置一个。 这个 aventara.config.ts 那个 aventara new 财务报告和审定财务报表 aventara init 写有一条: 它用它的
堆叠通过相同的 Logger,在错误级别,在此行之后.
[Nest] ERROR [Aventara] A3001 on Post.create.one (client origin, request 3f1c…): PrismaClientValidationError. The raw error goes to the framework's diagnostics sink.
[Nest] ERROR [Aventara] A3001 (request 3f1c…): raw error
PrismaClientValidationError: …
对于 A3004 (您的一条管子返回了服务器合同拒绝的参数),该行也命名了管子
操作,违法领域及其V码。 打电话的人只看过通用信息.
当模块从 config;一个框架,你建立自己 并通过作为
framework 保持准确的 diagnostics 你给的.
框架的验证最外:
| 快递 | 快捷化 | |
|---|---|---|
app.use(...) 中间软件, app.enableCors()头盔 | 运行 | 运行 |
| (全球与否) | 永远 | 永远 |
MiddlewareConsumer 中间软件 | 永远 | 运行, 因为 Fastify 在路由前运行 Nest 中间软件; 添加 .exclude("api/_*path") (你的电话) api 路径)使其远离 |
您的控制器在 <api>/_contract (或任何 _ 路径) | 从未达到 | 应用程序不启动 |
您的控制器在 <api>/health (外边) _(中文(简体) ) | 达到 | 达到 |
因此,授权属于Aventara管道(警卫)。 警卫看 请求的头条是
ctx.$transport.headers (小写名称,重复加入), 以及无论你的中间软件是 ctx.$state, (中文(简体) ),
通过模块 state 选项(下一节).
请求状态
state 种子每个协议请求 ctx.$state,该请求的每一个阶段都读取和写作。 没错
在协议接受之后,每个请求一次被呼叫,由平台自己请求(一个 stale 客户端或一个
未知路线从不叫它:
declare module "@aventara/core" {
interface AvState {
user?: { id: string; roles: string[] };
}
}
AventaraModule.forRoot({
config,
state: (req: Request) => ({ user: req.user }), // what your auth middleware set
});
// In the configuration: a guard reads it.
const signedIn: AvGuard = (ctx) => ctx.$state.user !== undefined;
forRootAsync是因为useFactory返回时state旁边framework或者说config。 。 。 .- 在快车,
req这是快递的要求。 在快速化时,它是快速化的request· ;app.use中间软件写入其 节点请求,请读request.raw.user。快速解答协议路径onRequest钩子,所以只有什么 在它之前运行( e)app.use中间软件,一个较早的onRequest钩子)已跑. - 使用
app.use(...)用于找到用户的中间软件(护照)authenticate电话、会话或JWT检查. 巢穴警卫从不运行 在协议路线上MiddlewareConsumer中间软件不在快取上(上表). - 与
access(访问提供者,参见:@aventara/core)种子状态本身:模块召唤 提供者的state与{ headers, request },平台请求头和请求. 传出否state旁边; 当 Nest 构建应用程序时, 模块拒绝对子, 并作为类型错误 aconfig那个 状态access。它通过config财务报告和审定财务报表framework数字 :AventaraModule.forRoot({ config: { ...config, access } })。 。 。 . - 掷出
new AvError("A4001", "…")从state拒绝请求403),任何其他抛出是500 A3000,与 错误diagnostics下潜. - 一个计划发给
/_transactions它的所有步骤和交易阶段都有一个状态。 一个Ctx叫舞台制造 继承它。 计算读取,但不能写. - 在每次调用过程中调用框架的服务器代码 :
framework.application.Post.create.one(args, { state: { user } })。 。 。 .
AvConfig.api 仅决定协议的安装地点. app.setGlobalPrefix(...) 不移动; 设置
api ,然后将生成的客户端指向同一个 URL.
请求
- 协议路线上的尸体是原始的 永远不要通过巢穴的身体剖析器
limits.maxRequestBytes编辑 协议的解码器会回答一个超大小的正体(413 A2010),另一个内容类型(415 A2011)和畸形的JSON (单位:千美元)400 A2000) (中文(简体) ). 你自己的路线保持巢穴的解析req.body。无NestFactory.create选项. - 对于
Content-Encoding, (中文(简体) ),gzip, (中文(简体) ),deflate财务报告和审定财务报表br被夸大,极限数为解码字节。 任何其他编码 答题415 A2011; 一个在编码中不解码的机构, 它的名字会回答400 A2000这两个答案包含 新铸造Aventara-Request-Id不是客户的. - 请求的ID是客户端的
Aventara-Request-Id当它到达, 或一个核心薄荷。 重写的中间软件req.headers无法改变它,和X-Request-Id从未复制到它,所以设置Aventara-Request-Id在你身边 代理服务器. - 查询字符串不是协议路径的一部分;它被忽略了.
MCP 终点
与 mcp 在配置中,模块还挂载了 MCP 服务器 @aventara/mcp 在这条路上,在快车和
既收紧又收紧日志 Aventara mounted the MCP endpoint at /mcp (…)。在此软件包旁安装它
@aventara/core'准确的版本 :
npm install --save-exact @aventara/mcp@<your @aventara/core version>
这是一个可选的同伴依赖关系:没有 mcp 不需要它,一个是 mcp 并且没有
软件包拒绝启动, 命名它和命令, 并填入:
AventaraModule: the configuration has `mcp`, and the package that serves it, @aventara/mcp, is not installed. Install it at @aventara/core's exact version (npm install --save-exact @aventara/[email protected]), or remove `mcp` from the configuration.
至于协议的路线,模块只挂载:端点正文被读取
原始,请求的URL来自平台 protocol 财务报告和审定财务报表 host (一组) trust proxy 运通或 trustProxy 打开
在一个代理后快速化,每个答案都是 @aventara/mcp'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''`'''''''''''''''''''''''''''' state 选项种子每个工具调用 ctx.$state。 。 。 .
见 @aventara/mcp用于工具、合同资源和授权的README.
平台差异
- Fastify 无法挂载一些资源密钥或
api路径作为文字路径。 需要的资源密钥*或一个 保留字符( e): + # $ & , / ; = ? @)在其路线上,或api路径包含*,启动时被拒绝 有一条信息命名它。 重命名它, 或者在快递上主机, 它挂载每个密钥 . - 快速路由是默认的对大小写不敏感,所以它也会在,比如说,下方回答协议,
/API/_contract· ; Fastify's not. 安全是没有的。 那是快递的case sensitive routing设定,默认关闭, 和你的改变。 这个 MCP端点是例外:在两个平台上,它只回答自己的路径,完全如所写(/mcp没有/MCP或者说/mcp/) (中文(简体) ). - 表达式回答
500 A3000以 %- 编码生成的客户端从未发送的协议路径 (例如%6Eotes页:1notes; 快速化和进程内协议执行. 这是已知的分歧. - 在快递上,反应带有快递
X-Powered-By页眉;app.disable("x-powered-by")将取去.
许可证
PolyForm盾 1.0.0 附加许可. 它可以自由使用,包括商业应用,但你
可能不会用来制造一种与阿文塔拉本身竞争的产品. 见 LICENSE 财务报告和审定财务报表
LICENSE-ADDITIONAL-PERMISSION.md。 。 。 .