恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Hasura Webhook 请求转换(Transforms):Actions/Event Triggers/Scheduled Triggers 的 Kriti 模板引擎实战指南
首页
资讯中心
/
Hasura Webhook 请求转换(Transforms):Actions/Event Triggers/Scheduled Triggers 的 Kriti 模板引擎实战指南
Hasura Webhook 请求转换(Transforms):Actions/Event Triggers/Scheduled Triggers 的 Kriti 模板引擎实战指南
发布时间:2026/9/20 22:26:24
Hasura Webhook 请求转换TransformsActions/Event Triggers/Scheduled Triggers 的 Kriti 模板引擎实战指南【免费下载链接】graphql-engineBlazing fast, instant realtime GraphQL APIs on all your data with fine grained access control, also trigger webhooks on database events.项目地址: https://gitcode.com/gh_mirrors/gr/graphql-engine本指南完整解读 graphql-engine 仓库 RFC《transforms》当 Hasura 生成的外部 HTTP 请求Actions、Event Triggers、Scheduled Triggers 的 webhook 调用与服务端期望的格式不一致时如何通过元数据中的transforms配置对请求方法、URL、请求体、Content-Type、查询参数与请求头进行模板化改写并借助内置调试端点验证转换结果。读完你将掌握transforms全部字段的语义与默认值、Kriti 模板语法字符串插值、for 循环、if/else、谓词函数、test_webhook_transform元数据 API 的调用方式以及底层实现原理。一、为什么需要请求转换Hasura 通过多种特性与外部 HTTP API 集成Actions动作、Event Triggers事件触发器、Scheduled Triggers定时触发器。这些外部 API 期望的客户端请求格式往往与 Hasura 自动生成的请求不同——例如外部服务希望请求体是特定的 JSON 结构、URL 中嵌入路径参数、使用GET方法携带查询参数或者使用application/x-www-form-urlencoded编码。为此RFC 提出在 Actions、Event Triggers 和 Scheduled Triggers 的配置中引入一个统一的transforms键在调用外部 API 之前对请求进行转换。该设计在仓库中已落地实现核心代码位于 server/src-lib/Hasura/RQL/DDL/Webhook/Transform.hs模块注释明确说明Webhook Transformations are data transformations used to modify HTTP Requests/Responses before requests are executed and after responses are received即既支持发送前改写请求也支持接收后改写响应ResponseTransform。二、transforms 配置规范2.1 顶层字段与默认值transforms的 Schema 由 RFC 定义如下字段语义与默认值均来自 RFC 原文字段类型含义与默认值request_methodRequestMethodGET/POST/PUT/PATCH/DELETE改写请求方法Nothing表示保持 POST 不变request_urlTemplateText改写请求 URLNothing表示保持原 URLrequest_bodyTemplateText改写请求体Nothing表示保持原始 payload。仅适用于 POST、PUT、PATCH 方法编码方式由request_content_type决定request_content_typeContentTypeJSON / XWWWFORM允许的 Content-Type 只有application/json默认与application/x-www-form-urlencoded两种query_paramsHashMap Text TemplateText向 URL 追加查询参数每个参数都会做 URL 编码request_headersTransformHeaders按remove_headers然后add_headers的顺序执行头转换某些头如Content-Type不允许转换应返回警告templating_engineTemplatingEngine使用的模板引擎默认go-basic落地实现为 KritiTransformHeaders的定义为addHeaders :: [(HeaderName, TemplateText)]添加/替换头与removeHeaders :: [HeaderName]移除头且求值顺序固定为先 removeHeaders 后 addHeaders。2.2 落地实现的 Schema 变化Kriti 与 version 字段RFC 中的go-basic模板语言在正式实现中演化为 Kriti 模板引擎。从 server/src-lib/Hasura/RQL/Types/Webhook/Transform.hs 的RequestTransform定义可见元数据实际支持的字段为version转换配置版本V1或V2缺省为 V1method、url、body、query_params、request_headers对应 RFC 中的request_method、request_url、request_body、query_params、request_headerstemplate_engine模板引擎默认Kriti。请求被拆分为五个可独立转换的维度HKD 记录RequestFieldsmethod、url、body、queryParams、requestHeaders。转换在运行时通过Transform类型类逐个字段应用applyRequestTransform完成整个 HTTP 请求的改写见 Transform.hs。2.3 运行时上下文RequestTransformCtx转换模板可引用的运行时数据由 server/src-lib/Hasura/RQL/DDL/Webhook/Transform/Request.hs 中的mkReqTransformCtx构造Kriti 执行时注入的上下文键为$body请求体JSON 解码后的值缺省为null$session_variables会话变量JSON$query_params原请求的查询参数若存在$base_url原始 URL以 JSON 字符串形式提供。runRequestTemplateTransform将上述上下文与 Kriti 函数一起传给KFunc.runKritiWith执行模板Request.hs。三、Kriti 模板语言RFC 中规划的go-basic是 Go 模板语言的子集包含面向对象与数组的字符串模板、for 循环、if/then/else 语句以及基本谓词函数、、、、||。以下语法示例均出自 RFC 原文。3.1 字符串模板String Templating适用于request_url或请求头转换。$url保存原始 URL可拼接路径参数# $url 为原始 URL $url/{{.event.author_id}}3.2 For 循环使用range标识符声明循环循环数组引用写在range之后当前数组元素以$引用循环以end结束{ articles: [ {{ range .event.author.articles }} { id: {{$.id}}, title: {{$.title}} } {{ end }} }还支持带索引的枚举语法将数组元素绑定为$article、下标绑定为$index绑定仅在循环作用域内有效{{ $index, $article : .event.author.articles }}3.3 If/Then/Else 与谓词函数{{if $index}} , {{$article}} {{else}} {{$article}} {{end}}基本谓词函数$.x $.y $.x $.y $.x $.y $.x $.y $.x || $.y3.4 完整示例以下模板同时使用路径访问、range 循环带索引与 if 判断RFC 原文提供的完整示例{ author: { name: {{$.event.name}}, age: {{$.event.age}}, articles: [ {{range $index, $article : $.event.author.articles}} {{if $index 5}} , {{$article}} {{else}} {{$article}} {{end}} {{end}} ] } }注意正式实现的 Kriti 引擎在模板语法上与 RFC 中的go-basic略有差异例如循环绑定的写法使用前建议通过下文的test_webhook_transform或validate端点验证模板。四、实现原理扩展 JSON 值与求值器RFC 给出了底层实现构想定义一个扩展的 JSON 值类型ValueExt在 Aeson 核心项Object/Array/String/Number/Boolean/Null之外增加路径访问、条件、比较、逻辑与循环等原语data Accessor Obj String | Arr Int deriving (Show, Eq) data ValueExt -- Core Aeson Terms Object (M.HashMap Text ValueExt) | Array (V.Vector ValueExt) | String Text | Number Scientific | Boolean Bool | Null -- Extended Terms | Path [Accessor] | Iff ValueExt ValueExt ValueExt | Eq ValueExt ValueExt | Gt ValueExt ValueExt | Lt ValueExt ValueExt | AND ValueExt ValueExt | OR ValueExt ValueExt | Member ValueExt ValueExt | Range (Maybe Text) Text [Accessor] ValueExt -- ^ {{ range i, x : $.foo.bar }} deriving (Show, Eq, Read)Range构造器对应{{ range i, x : $.foo.bar }}循环语法。RFC 同时给出了一个 JSON 模板及其解析后的ValueExtAST 示例exampleJson/exampleAst演示Path与Range如何被解析为树结构。求值器的签名被定义为eval :: ValueExt - Value - Either Err Value由于 JSON 是无类型的RFC 特别列出了运行时可能发生的三类错误谓词作用于非布尔值循环声明中的模板引用不是数组数组越界访问或对象未定义键访问。这些错误只能等到运行时才能被发现并报错。落地实现中Kriti 错误会被包装进TransformErrorBundle并附带error_code、source_position行列号与message等信息返回给用户见下文调试端点输出。五、transforms 配置示例RFC 提供了 5 个最小可用的配置示例全部继承如下YAML 元数据片段。5.1 转换请求体从请求体取值构造新 JSON 结构也可引用会话变量transforms: request_body: { key1: {{$.value1}}, key2: {{$.value2}}, key3: {{$.session[x-hasura-user-id]}} }5.2 基于请求体数据转换 URL$url保留原始 URL后面拼接路径参数transforms: request_url: $url/{{$.input[country]}}5.3 转换请求方法并映射查询参数所有 webhook 集成默认都是 POST可转为GET此时 payload 会映射到查询参数并做 URL 编码transforms: request_method: GET query_params: param1: {{$.value1}} param2: {{$.value2}}5.4 转换请求头先移除头再添加头RFC 明确 remove 先于 add下面示例从事件 payload 添加自定义头并改写User-Agenttransforms: request_headers: add_headers: [{x-cutom-id: {{$.event.user_id}}}, { User-Agent: myapp-server}] remove_headers: [User-Agent]5.5 转换为 x-www-form-urlencoded请求体仍以 JSON 书写但request_content_type指定为x-www-form-urlencoded每个顶层字段/值被转换为一个表单参数值做 URL 编码transforms: request_body: { key1: {{$.value1}}, key2: {{$.value2}}, key3: {{$session.x-hasura-user-id}} } request_content_type: x-www-form-urlencoded上述配置会将请求体转换为key1{{.value1}}key2{{.value2}}key3{{$session.x-hasura-user-id}}的形式。控制台可以展示给定转换的输出使用户清楚最终进入请求体的内容。在落地实现中表单编码转换由 server/src-lib/Hasura/RQL/DDL/Webhook/Transform/Body.hs 的foldFormEncoded完成按keyvalue以连接键值均做 URI 转义且值为null的字段会被跳过查询参数转换QueryParams.hs支持AddOrReplace逐项求值并追加/替换与ParamTemplate先求值整段模板再用parseQuery解析两种模式请求头转换Headers.hs实现为先按remove_headers过滤、再把add_headers求值后拼接。六、真实案例通过 Action 集成 Notion APIRFC 给出了一个真实场景——通过 Hasura Action 集成 notion.so API。Action 定义GraphQL Schematype Mutation { addNotionItem ( databaseId: String! properties: jsonb! children: jsonb ): ItemOutput } type ItemOutput { id : String! object : String! created_time : timestamptz! last_edited_time : timestamptz! }对应的 transform把 Action 输入映射到 Notion 期望的请求体结构children为空时输出nullrequest_body: { parent: { database_id: {{$.action.input.databaseId}}, }. properties: {{$.action.input.properties}}, children: {{ if $.action.input.children }} {{$.action.input.children}} {{ else }} null {{ end }} }七、调试与验证test_webhook_transform 元数据 APIRFC 说明服务器提供v1/metadata类型的test_webhook_transformer接收 transformer spec 与 payload返回转换后的请求。该能力在仓库中以test_webhook_transform元数据 API 落地见 server/src-lib/Hasura/Server/API/Metadata/Instances.hs并有完整的 pytest 用例验证例如 server/tests-py/queries/v1/metadata/test_webhook_transform_success.yamlURL/方法/头/查询参数全量转换、test_webhook_transform_success_form_urlencoded.yamlx_www_form_urlencoded表单编码、test_webhook_transform_bad_parse.yaml模板解析失败与 test_webhook_transform_bad_eval.yaml模板求值失败。7.1 基本调用示例RFC 原文type: test_http_transformer args: webhook_url: http://httbin.org payload: event: user_id: 1 username: bob password: cat transformer: request_method: GET request_url: $url/{{event.user_id}} query_params: param1: {{$.event.username}} param2: {{$.event.password}}输出request_method: GET request_url: http://httbin.org/1?param1bobparam2cat7.2 落地实现中的调用格式与成功/失败输出RFC 同时给出了控制台编辑器集成所需的第二个端点validate_go-basic_template落地为test_webhook_transform输入模板与 JSON 数据端点会尝试解析并求值模板并返回结果{ type : test_webhook_transform, args : { webhook_url: https://localhost:1234, body: { hello: world }, request_transform: { body: {{ $.hello }}, template_engine: Kriti } } }成功输出{ payload: world, headers: [ [ content-type, application/json ] ], method: GET, webhook_url: https://localhost:1234/ }失败输出Kriti 类型错误携带error_code、source_position行列号与错误消息{ payload: { error_code: TypeErrorCode, source_position: { end_column: 15, start_line: 1, end_line: 1, start_column: 12 }, message: Type Error: Expected object }, headers: [ [ content-type, application/json ] ], method: GET, webhook_url: https://localhost:1234/ }实际 pytest 用例展示了更完整的请求转换元数据格式request_transform下包含version、url、template_engine、body、method、query_params、request_headers等字段其中body在 V2 版本下进一步拆分为action如transform/x_www_form_urlencoded与template/form_template。例如表单编码用例将{hello: world}经模板foo: bar、baz: {{$body.hello}}转换为请求体bazworldfoobar并自动带上content-type: application/x-www-form-urlencoded见 test_webhook_transform_success_form_urlencoded.yaml。八、控制台开发体验Console DXRFC 指出控制台的开发体验对该功能成败至关重要需要一种用样本数据测试模板的方式。为此控制台需要为给定的 webhook 生成样本数据payload然后将用户提供的模板交给上文描述的调试端点执行从而在界面上直接呈现转换结果。这让用户无需真正发起外部请求即可确认转换后的请求体、URL、方法与头是否符合预期。九、备选方案与设计取舍RFC 的 Alternatives 章节提到曾考虑使用jsonnet作为替代模板语言最终选择go-basic后落地为 Kriti的主要理由是语法更简单、学习成本更低。从实现看Kriti 面向 JSON 构造场景设计与 Aeson 的值模型天然契合参考 server/src-lib/Data/Aeson/Kriti/Functions.hs并在 Actions、Event Triggers、Scheduled Triggers 以及连接模板如 Hasura/Backends/Postgres/Execute/ConnectionTemplate.hs等多处复用。十、实践要点小结明确默认行为所有 webhook 集成默认 POST application/json不设置某字段即保持原始值。注意方法约束request_body仅对 POST、PUT、PATCH 生效转GET时请将数据映射到query_params自动 URL 编码。表单编码约定x-www-form-urlencoded下仍以 JSON 写request_body顶层字段逐一转为表单参数值为null的字段会被跳过。头部转换顺序先remove_headers后add_headersContent-Type等关键头不允许转换。善用调试端点任何模板改动都应先用test_webhook_transform以样本数据验证可参考 server/tests-py/queries/v1/metadata/ 下以test_webhook_transform_*命名的 YAML 用例构造输入错误响应中的source_position可直接定位模板中的行列。牢记运行时错误Kriti 求值可能因类型不匹配、循环引用非数组、越界/未定义键访问而失败这类错误只能在运行时暴露。【免费下载链接】graphql-engineBlazing fast, instant realtime GraphQL APIs on all your data with fine grained access control, also trigger webhooks on database events.项目地址: https://gitcode.com/gh_mirrors/gr/graphql-engine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考