使用 AWS Lambda 作为 Hasura GraphQL Engine 事件触发器 Webhook 的 Node.js 实战指南
【免费下载链接】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
导读
本文基于 Hasura GraphQL Engine 官方仓库中的 aws-lambda/nodejs8/echo 示例,完整讲解如何把 AWS Lambda 函数接入 Hasura Event Trigger,在数据库发生INSERT/UPDATE/DELETE操作时自动接收事件负载并执行异步业务逻辑。读完本文,你将掌握事件触发负载(payload)的结构、Lambda 的创建与配置步骤、index.handler的编写方式,以及如何在 Hasura Console 中把 Lambda 的 API 端点注册为 Webhook,并了解在此基础上扩展出"写回数据库"等高级用法的实现思路。
什么是 Event Trigger,为什么选择 AWS Lambda
Hasura Event Trigger 是 Hasura GraphQL Engine 提供的一种异步业务逻辑机制:当连接的数据库中某张表发生数据变更时,Hasura 会以 HTTPPOST请求将事件负载发送到你配置的 Webhook 端点(参见 docs/docs/event-triggers/overview.mdx)。Event Trigger 可配置为在表的INSERT、UPDATE、DELETE事件上触发,也可以在 Console 中通过按钮手动触发。
把 Webhook 部署在 AWS Lambda 上的好处在于:无需维护常驻服务器,按调用次数计费,天然适合"数据库事件 → 异步处理"这种低频、突发的调用模式。仓库 community/boilerplates/event-triggers/README.md 给出的整体架构如下:
从源码目录看,aws-lambda 下按语言组织了多个官方示例,Node.js(nodejs8)目录下包含两个用例:
| 目录 | 用途 |
|---|---|
echo | 回显触发器负载(本文主体) |
mutation | 在收到 insert 事件后,用 GraphQL mutation 向数据库写回关联数据 |
第一步:在数据库中准备notes表
Event Trigger 必须挂在某张表上。官方示例使用一张简单的notes表,你可以在 Hasura Console 的 Data 页签中创建,也可以在 SQL 中执行建表语句。字段定义如下:
notes: id: int note: text字段含义:
id:整数类型,作为主键(示例中默认为自增,mutation 示例中明确为Integer auto-increment);note:文本类型,存放便签内容。
该表将成为后面验证事件负载的载体:插入、更新、删除notes表的数据时,对应的 Lambda 就会被触发。
第二步:理解 Event Trigger 的 Webhook 负载结构
在动手写 Lambda 之前,先理解 Hasura 发送给 Webhook 的数据格式,这是正确解析index.js中event.body的关键。官方文档 docs/docs/event-triggers/payload.mdx 定义了完整的负载结构:
{ "payload": { "created_at": "<TIMESTAMP>", "delivery_info": { "current_retry": "<RETRY_NUMBER>", "max_retries": "<MAX_RETRIES>" }, "event": { "data": { "new": "<OBJECT_OF_COLUMNS_AND_VALUES>", "old": "<OBJECT_OF_COLUMNS_AND_VALUES|NULL>" }, "op": "<INSERT|UPDATE|DELETE|MANUAL>", "session_variables": { "x-hasura-role": "<ROLE_NAME>" }, "trace_context": { "span_id": "<SPAN_ID>", "trace_id": "<TRACE_ID>" } }, "id": "<UUID_FOR_INVOCATION>", "table": { "name": "<TABLE_NAME>", "schema": "<SCHEMA_NAME>" }, "trigger": { "name": "<TRIGGER_NAME>" } } }核心字段说明(完整字段表见 payload.mdx):
| 字段 | 类型 | 说明 |
|---|---|---|
created_at | String | 触发器被调用的时间戳 |
delivery_info.current_retry/max_retries | Integer | 当前重试次数 / 最大重试次数 |
event.op | String | 操作名,取值仅为INSERT、UPDATE、DELETE、MANUAL |
event.data.new | Object | 事件相关的新数据,键值对为"列名-值" |
event.data.old | Object 或null | 事件相关的旧数据,不适用时为null |
event.session_variables | Object | 触发事件的用户的 session 变量(Postgres 下可用) |
id | String | 本次调用的 UUID |
table.name/table.schema | String | 表名 / schema 名 |
trigger.name | String | 触发器名 |
不同操作下new/old的取值规则(Postgres):
INSERT:old为null,new为插入的行;UPDATE:old为更新前的值,new为更新后的值;DELETE:old为被删除的行,new为null;MANUAL:old为null,new为当前行。
此外,官方文档还说明:UPDATE事件只在"新数据与旧数据不同"时才投递;表计算字段不会包含在负载中。Webhook 返回2xx状态码被视为调用成功,其他状态码会按重试配置触发重试;若响应携带Retry-After头且状态码为非2xx,则事件会在指定秒数后再次投递,可用于限流/防抖。
第三步:在 AWS 上创建 Lambda 函数
按 echo/README.md 的步骤在 AWS 控制台完成配置:
- 在 AWS Lambda 控制台创建一个函数;
- 运行时选择Node.js 8.10;
- 选择"start from scratch"(从头开始创作);
- 添加API Gateway作为触发器;
- 在 API Gateway 中新建一个 API(这样 Lambda 就获得了一个可供 Hasura 调用的 HTTP 端点);
- 把下文 index.js 的代码粘贴进函数编辑器,处理函数即为
index.handler。
环境说明:示例基于 Node.js 8.10 编写(当时 Lambda 的主流运行时)。当前 AWS 已提供更新的 Node.js 运行时,代码中的 async/await 语法在更高版本中同样可用,运行时版本升级不影响示例逻辑本身。
第四步:深入解析index.handler源码
这是本次示例的核心文件:echo/index.js。它演示了如何解析 Hasura 事件负载并按操作类型回显消息:
exports.handler = async (event) => { let response = {} try { let { table: { name }, event: { op, data } } = event.body; response.statusCode = 200; if (name === "notes" && op === "INSERT") { response.body = `New note ${data.new.id} inserted, with data: ${data.new.note}`; } else if (name === "notes" && op === "UPDATE") { response.body = `Note ${data.new.id} updated, with data: ${data.new.note}`; } else if (name === "notes" && op === "DELETE") { response.body = `Note ${data.old.id} deleted, with data: ${data.old.note}`; } return response } catch (e) { response.statusCode = 400; response.body = "cannot parse hasura event"; return response } };逐段解读其设计:
1. 从event.body解构负载
let { table: { name }, event: { op, data } } = event.body;注意这里解构的是event.body(Lambda 通过 API Gateway 收到的 HTTP 请求体),而不是event本身。对照上面的负载结构:table.name对应payload.table.name,event.op对应payload.event.op,data对应payload.event.data。需要强调的是,仓库中这份示例对event.body直接解构,说明作者在创建 API Gateway 触发器时未开启 Lambda 代理集成(若开启代理集成,event.body会是 JSON 字符串,需要先JSON.parse才能解构);你在实际部署时需根据自己的 API Gateway 配置做相应调整。
2. 按操作类型分支处理
INSERT:data.new携带新行,读取data.new.id和data.new.note;UPDATE:data.new携带更新后的行;DELETE:此时data.new为null,必须读取data.old.id与data.old.note——这是示例中体现"不同操作读取不同数据段"的关键细节。
3. 异常兜底
整个解构与分支放在try/catch中,一旦负载格式不符(例如 Webhook 被非 Hasura 的请求调用),返回400与"cannot parse hasura event"。这符合官方对 Webhook 响应约定:2xx表示成功,其他状态码会触发重试或告警。
第五步:在 Hasura Console 中添加 Event Trigger
完成 Lambda 部署并记下 API Gateway 的端点 URL 后,回到 Hasura Console 配置触发器:
- 进入Events页签,点击添加触发器;
- 选择
notes表,并勾选全部操作:insert、update、delete; - 把 AWS Lambda 的API 端点 URL粘贴为Webhook地址;
- 保存后,触发器的状态变为活跃(active)。
至此,链路打通:任何对notes表的插入、更新、删除操作,都会触发 Hasura 向你的 Lambda 端点发送POST请求,Lambda 解析负载并按操作类型返回响应消息。可以在 Console 的 Events 页签中看到每次事件的投递记录、响应状态码与重试信息,用于验证整条链路是否工作正常。
进阶:在 Lambda 中通过 GraphQL Mutation 写回数据库
echo示例只做"回显",同目录下的 mutation/index.js 展示了更有业务价值的模式:在收到事件后,调用 Hasura 的 GraphQL 端点向数据库写入关联数据。其部署步骤比 echo 多两步(详见 mutation/README.md):
- 在
mutation目录执行npm install安装依赖(使用node-fetch发起 HTTP 请求); - 执行
zip -r hge-mutation.zip .打成 zip 包,在 Lambda 中上传该 zip(而非粘贴代码); - 在 Lambda 环境变量中配置两项:
ACCESS_KEY:你初始化 Hasura GraphQL Engine 时配置的 admin secret;HGE_ENDPOINT:HGE 实例运行的 URL。
代码的核心逻辑是:当notes表发生 insert 事件时,用旧行数据构造一个 GraphQL mutation,把note_revision表的记录写回数据库:
const query = ` mutation updateNoteRevision ($noteId: Int!, $data: String!) { insert_note_revision (objects: [ { note_id: $noteId, note: $data } ]) { affected_rows } } `; exports.handler = async (event) => { try { const qv = { noteId: event.body.event.data.old.id, data: event.body.event.data.old.note }; const result = await fetch(hgeEndpoint + '/v1/graphql', { method: 'POST', body: JSON.stringify({ query: query, variables: qv }), headers: { 'Content-Type': 'application/json', 'x-hasura-admin-secret': accessKey }, }); // ... } };这段代码演示了三个要点:
- 用环境变量保存敏感信息:
ACCESS_KEY与HGE_ENDPOINT通过process.env读取,避免硬编码; - 以 admin secret 调用 GraphQL API:请求头中的
x-hasura-admin-secret用于绕过权限校验执行写操作; - mutation 携带变量:
variables中的noteId、data与 GraphQL 查询的$noteId、$data一一对应,而数据来源正是事件负载中event.data.old的旧行。
对应的note_revision表需要在 Hasura Console 中预先创建:
Table name: note_revision Columns: id: Integer auto-increment note: Text note_id: Integer (foreign key to notes.id) update_at: Timestamp, default `now()`扩展阅读与仓库参考
- 事件触发器官方文档:事件触发负载结构见 docs/docs/event-triggers/payload.mdx,总览见 docs/docs/event-triggers/overview.mdx;
- AWS Lambda 目录总览与多语言支持情况见 community/boilerplates/event-triggers/aws-lambda/README.md,同一用例还提供 Go、Python、Ruby 等语言实现(如 python/echo/echo.py);
- 其他 Serverless 平台示例:仓库还包含 Azure Functions、Google Cloud Functions、Netlify Functions、Zeit Now 等平台的同款 boilerplate,可对照参考。
本文的完整示例代码与部署文档均位于仓库 community/boilerplates/event-triggers/aws-lambda/nodejs8/ 目录下,可直接查看、复制并据此部署。
【免费下载链接】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),仅供参考