使用 Azure Functions(Node.js)与 Hasura 事件触发器实现 GraphQL 回写:mutation 示例实践指南
使用 Azure FunctionsNode.js与 Hasura 事件触发器实现 GraphQL 回写mutation 示例实践指南【免费下载链接】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仓库中 Azure Functions Node.js mutation 示例在 Azure Functions 上部署一个 HTTP 云函数作为 Hasura 事件触发器的 Webhook在数据库发生 insert/update/delete 时接收事件负载并通过 GraphQL mutation 将关联数据回写到 Postgres 数据库。读完本文你将掌握事件触发器负载结构解析、Azure Functions 部署与配置、以及数据库变更 → 事件触发 → 函数执行 → GraphQL 回写的完整落地链路。事件触发器与 Serverless 函数的协作模型在深入示例代码之前先明确该示例在整个项目中的位置。graphql-engine仓库为事件触发器提供了面向多种 Serverless 平台AWS Lambda、Google Cloud Functions、Azure Functions、Zeit Now、Netlify Functions的样板代码统一组织在 community/boilerplates/event-triggers 下按平台 → 语言 → 用例的目录结构排列community/boilerplates/event-triggers/ ├── azure-functions/ │ └── nodejs/ │ ├── echo/ # 回显事件负载帮助理解 payload 结构 │ └── mutation/ # 在事件中通过 GraphQL mutation 回写关联数据 ├── aws-lambda/ ├── google-cloud-functions/ ├── netlify-functions/ └── zeit-now/这些样板函数实现了 Hasura GraphQL Engine 在数据库 insert、update、delete 时可触发的异步业务逻辑用例。整体工作流如下面的架构图所示APIs、后台任务、GraphQL mutations 将数据写入 PostgresHasura events 监听这些数据库变更并触发右侧的 Serverless 函数或微服务本示例正是架构图中Hasura events → Serverless function这一环的具体实现事件发生时Hasura 将带完整数据的 HTTP 请求事件负载POST 到 Azure Function 的 URL函数解析负载后再通过 GraphQL mutation 把派生数据写回数据库。前置准备数据表结构与事件语义建表notes 与 note_revision示例的业务场景是笔记修订审计每当notes表中的笔记被修改就在note_revision表中追加一条修订记录。为此需要两张表字段定义如下原文完整继承Table name: notes Columns: id: Integer auto-increment note: Text Table name: note_revision Columns: id: Integer auto-increment note: Text note_id: Integer (foreign key to notes.id) update_at: Timestamp, default now()其中note_revision.note_id外键关联到notes.idupdate_at使用数据库默认值now()自动记录修订时间。这两张表需要在你已运行 Hasura GraphQL EngineHGE的 Postgres 数据库中创建——该样板假设你已有一个 HGE 实例在运行若尚未搭建可先完成 Postgres HGE 的安装再继续。理解事件负载中的 op 与 data事件触发器的 HTTP 请求体包含event与table两个顶层字段其中event.op表示触发操作INSERT/UPDATE/DELETEevent.data包含该操作的旧值与新值。Node.js 函数用以下代码解构const { event: {op, data}, table } req.body;data.newinsert/update 操作后的新行数据data.oldupdate/delete 操作前的旧行数据。这一负载结构是编写一切事件处理器的基础如果希望更直观地观察完整 payload可以参考同目录下的 echo 示例它直接回显整个请求体。核心实现在事件处理器中执行 GraphQL mutation函数源码逐段解读mutation 示例的入口代码位于 HTTPTrigger/index.js通过graphqurl库向 HGE 发起 GraphQL 请求。读取环境变量ADMIN_SECRET与HGE_ENDPOINT从进程环境读取分别用于携带 admin 密钥鉴权与定位 GraphQL 端点const { query } require(graphqurl); const ADMIN_SECRET process.env.ADMIN_SECRET; const HGE_ENDPOINT process.env.HGE_ENDPOINT;定义 mutation当笔记更新时用旧值向note_revision插入一条修订记录这正是写回数据库的核心逻辑const MUTATION_NOTE_REVISION mutation updateNoteRevision ($noteId: Int!, $data: String!) { insert_note_revision (objects: [ { note_id: $noteId, note: $data } ]) { affected_rows } } ;处理事件并回写解构负载后取data.old.id作为noteId、data.old.note作为data调用graphqurl的query函数向HGE_ENDPOINT /v1/graphql发起带 admin 密钥的 mutation成功时返回 200 与响应体失败时返回 500 与错误详情module.exports function (context, req) { context.log(JavaScript HTTP trigger function processed a request.); try { context.log(Inside); const { event: {op, data}, table } req.body; context.log(data); context.log(data.new.id); const qv {noteId: data.old.id, data: data.old.note}; const hgeResponse query({ query: MUTATION_NOTE_REVISION, endpoint: HGE_ENDPOINT /v1/graphql, variables: qv, headers: { x-hasura-admin-secret: ADMIN_SECRET } }).then((response) { context.log(response); context.log(After query); context.res { body: { error: false, data: response } }; context.done(); }).catch((error) { console.error(JSON.stringify(error)); context.res { status: 500, body: { error: true, data: JSON.stringify(error) } }; context.done(); }); } catch(e) { context.res { status: 400, body: An error occured. }; context.done(); } };值得注意的工程细节使用data.old而非data.new该用例语义是记录变更前的状态因此即使触发操作是 UPDATE也回写旧值。若你的场景需要处理 DELETEdata.new会为空此时必须以data.old为准——从源码结构看该模板主要面向 insert/update 事件做修订记录尚未对 delete 场景单独分支处理。鉴权方式通过x-hasura-admin-secret请求头携带 admin 密钥调用 HGE 的/v1/graphql端点。实际生产环境中应考虑更细粒度的权限而非直接使用 admin secret。同步上下文语义Azure Functions 的 Node.js 模型要求调用context.done()结束执行query返回 Promise因此在.then/.catch中分别设置context.res与context.done()。函数绑定与依赖配置function.json定义了 HTTP 触发器的绑定authLevel为anonymous允许get与post两种方法入口参数名req、出口绑定res详见 function.json。由于事件触发器以 POST 方式投递负载函数必须接受 POST 请求。package.json声明了两个运行时依赖package.jsongraphqurl^0.3.1Hasura 官方 GraphQL 客户端封装了query便捷方法自动处理请求与响应解析node-fetch^2.2.0低层 HTTP 客户端graphqurl 的依赖之一示例亦将其显式声明。host.json中的关键配置host.jsonfunctions: [HTTPTrigger]指定本函数应用仅托管 HTTPTrigger 这一个函数routePrefix: 移除默认api/路由前缀使函数可直接通过根路径访问便于粘贴为 webhook URLfunctionTimeout: 00:00:30函数超时 30 秒version: 2.0Azure Functions 运行时版本 2.x。在 Azure 上创建并发布函数应用准备好HTTPTrigger目录含index.js、function.json、package.json、sample.dat后在 Azure CLI 中依次执行以下命令完成资源创建与发布命令与原文保持一致az group create --name my-functions-group --location southindia az storage account create --name myfunctionsstorage --location southindia --resource-group my-functions-group --sku Standard_LRS az functionapp create --name myfunctionsapp --storage-account myfunctionsstorage --resource-group my-functions-group --consumption-plan-location southindia func azure login func azure subscriptions set Free Trial func azure functionapp publish myfunctionsapp逐步说明az group create在southindia区域创建资源组my-functions-groupaz storage account create创建配套存储账户Standard_LRS标准本地冗余存储Azure Functions 依赖它保存函数代码与运行时状态az functionapp create创建消费计划Consumption Plan下的函数应用myfunctionsapp按执行计费、无需预置服务器func azure login/func azure subscriptions set Free TrialCore Tools 登录 Azure 并选择订阅示例使用订阅名为Free Trialfunc azure functionapp publish myfunctionsapp将本地函数应用发布到云端。配置环境变量与鉴权发布完成后必须为函数应用配置两个环境变量ADMIN_SECRETHGE 的 admin secret函数用它通过x-hasura-admin-secret头调用 GraphQL APIHGE_ENDPOINTHGE 实例的端点函数在端点后拼接/v1/graphql发起请求。若函数应用的 Authorization level 已启用非anonymous还需要在调用时附带X-Function-Key请求头——即配置function.json中的authLevel后客户端调用需携带函数密钥。本地运行与日志排查本地运行在函数目录执行func host start即可在本地启动 Functions 运行时便于用事件负载样本调试。仓库中的sample.dat提供了请求体样本数据。查看云端日志执行func azure functionapp logstream myfunctionsapp实时流式查看线上日志结合源码中的context.log输出如context.log(data)、context.log(response)排查事件负载解析或 mutation 执行问题。在 Hasura 中创建事件触发器并关联 Webhook最后一步是把函数 URL 绑定为 Hasura 事件触发器步骤原文完整继承打开 Hasura Console 的Events事件标签页添加一个触发器为触发器选择全部insert、update、delete操作将函数应用的 URL 粘贴为webhook。完成配置后事件触发链路即告打通数据库写入insert/update/delete → Hasura 监听变更并捕获事件 → POST 事件负载到 Azure Function Webhook → 函数解析 event.data携带 admin secret 调用 /v1/graphql → 执行 insert_note_revision mutation 回写修订记录与仓库中其他示例的对照在 event-triggers 样板目录 中本 mutation 示例与两个近亲形成递进关系echo 示例azure-functions/nodejs/echo原样回显请求体仅用于理解事件负载结构与解析方式不产生任何数据库副作用mutation 示例本文在 echo 基础上更进一步解析负载后主动通过 GraphQL 向数据库回写关联数据是异步业务逻辑回写数据库的标准范式仓库还计划了异步推送通知FCM/APNS与ETL/数据转换如更新 Algolia 索引等更多用例说明事件触发器可用于通知、索引、审计等多种异步场景。小结与生产实践建议本文基于 mutation/README.md 及 HTTPTrigger/index.js 源码完整走通了建表 → 编写 Azure Function → 发布与配置 → 绑定 Hasura 事件触发器的流程。落地到生产环境时建议关注以下几点鉴权加固当前函数依赖x-hasura-admin-secret调用 HGE且function.json中authLevel为anonymous生产环境应启用函数级鉴权配置X-Function-Key并为 HGE 选用更细粒度的权限体系幂等与重试Hasura 事件投递可能因网络失败重试mutation 回写逻辑应具备幂等性避免重复插入产生脏数据错误可观测善用context.log与logstream在.catch分支记录完整错误 JSON便于定位失败原因超时与并发注意functionTimeout为 30 秒GraphQL 回写应控制在该时限内完成消费计划下函数实例会自动弹性伸缩。该示例为数据库变更驱动的异步逻辑提供了可直接复用的最小实现无论是修订审计、通知推送还是数据同步都可以以此为起点扩展出符合自身业务的事件处理服务。【免费下载链接】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),仅供参考