在 Google Cloud Functions 上部署 Hasura 远程 GraphQL Schema:Node.js + Apollo Server 实战指南
【免费下载链接】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 仓库中的官方样板代码(boilerplate)为核心,讲解如何用 Node.js + Apollo Server 编写一个自定义 GraphQL 服务,并将其部署到 Google Cloud Functions,最终与 Hasura 自动生成的 GraphQL API 合并(Merge)为统一端点。读完本文,你将掌握该样板代码的文件结构与 Schema/Resolver 实现、本地开发流程、gcloud部署命令,以及通过 Console / CLI / Metadata API 三种方式把远程服务接入 GraphQL Engine 的完整链路。
为什么需要"远程 Schema"样板代码
Hasura GraphQL Engine 的核心能力是围绕数据库自动生成 CRUD + 实时 GraphQL API,并内置细粒度授权与访问控制。但业务中常常存在无法用数据库 CRUD 直接表达的逻辑——例如支付 API、调用天气等第三方数据源、在插入前执行校验的定制 Mutation。remote-schemas.md(仓库根目录)明确给出了远程 Schema 的典型适用场景:
- 定制 Mutation(例如在 insert 前执行业务校验);
- 在 GraphQL Engine 的统一 API 之后接入支付等功能,提供一致的访问接口;
- 从其他数据源拉取异构数据(如天气 API 或另一个数据库)。
实现方式也很直接:自行编写一个任意语言/框架的 GraphQL 服务,把 HTTP 端点交给 Hasura,由 GraphQL Engine 自动完成 Schema 合并(schema stitching)。community/boilerplates/remote-schemas/README.md中给出了面向不同云平台的样板代码索引:AWS Lambda、Azure Functions、Google Cloud Functions、Zeit Now 等。本文聚焦其中 Google Cloud Functions 下的 Node.js 版本,其样板代码位于 community/boilerplates/remote-schemas/google-cloud-functions/nodejs。
整个合并机制可用下图概括:业务侧 GraphQL 服务与 Hasura 生成的 API 统一暴露给客户端,由 GraphQL Engine 在中间完成 Schema 拼接与转发。
样板代码目录结构与技术栈
先看该 boilerplate 的文件构成(相对仓库根目录):
community/boilerplates/remote-schemas/google-cloud-functions/nodejs/ ├── README.md # 官方使用说明(本文的原始依据) ├── index.js # Schema(typeDefs) 与 Resolvers 定义 ├── localDev.js # 本地开发服务器入口(Apollo Server) ├── googleCtx.js # Cloud Functions 部署入口(导出 handler) └── package.json # 依赖清单(main 指向 googleCtx.js)按 README.md 的记录,该样板的技术栈为:
- 运行时:Node.js 8.10(对应部署命令中的
--runtime nodejs8,这是样板代码创作时代的记录;如今 Google Cloud Functions 的运行时版本已迭代多代,实际部署时应选用当时仍受支持的 Node 运行时并相应调整依赖版本); - 平台:Google Cloud Functions(HTTP 触发的 serverless 函数);
- 框架/库:Apollo Server(GraphQL 服务框架),Cloud Functions 场景下使用其适配包
apollo-server-cloud-functions。
依赖声明位于 package.json:
{ "name": "google-cloud-functions-nodejs", "version": "1.0.0", "main": "googleCtx.js", "dependencies": { "apollo-server-cloud-functions": "^2.4.8", "graphql": "^0.13.1", "graphql-tag": "^2.10.1" } }注意main字段指向googleCtx.js,说明面向 Cloud Functions 的真正部署入口是googleCtx.js中导出的handler;而index.js只是被复用的 Schema/Resolver 定义模块。这种"定义与运行环境分离"的结构,正是同一套 GraphQL 定义既能本地调试、又能无改动部署到云函数的可移植设计。
Schema 与 Resolver:最小可运行的 GraphQL 定义
核心业务定义全部收敛在 index.js 中:
const gql = require('graphql-tag'); const typeDefs = gql` type Query { hello: String } `; const resolvers = { Query: { hello: () => "world", }, }; exports.typeDefs = typeDefs; exports.resolvers = resolvers;要点拆解:
typeDefs使用graphql-tag的模板标签语法声明 Schema,只有一个Query.hello: String根字段;resolvers为hello提供实现() => "world";- 通过
exports导出typeDefs与resolvers,供本地开发与云函数两个入口复用。
对一个将要接入 Hasura 的远程 Schema 而言,这段代码虽小,却体现了三个必须满足的契约:
- 必须是标准 GraphQL Schema,且类型名与字段名全局唯一(Hasura 合并远程 Schema 时要求跨所有合并 Schema 类型名/节点名唯一,详见 remote-schemas.md 的 Caveats 一节);
- 必须提供HTTP 端点供 GraphQL Engine 转发请求;
- 生产环境中通常会继续扩展
Mutation、加入鉴权 headers、接入数据库等,样板只演示最小闭环。
本地开发:一条命令起一个 GraphQL Playground
在进入云函数部署之前,先在本地验证 Schema 与 Resolver 的行为。README.md 给出的流程是:
# 进入样板目录 cd community/boilerplates/remote-schemas/google-cloud-functions/nodejs # 安装依赖(--no-save 表示不写入 package.json,示例环境可按需调整) npm i --no-save apollo-server # 启动本地开发服务器 node localDev.js启动成功后控制台输出Server ready at http://localhost:4000/,浏览器访问localhost:4000即可打开 GraphQL Playground,执行:
{ hello }得到{ "data": { "hello": "world" } }。
本地入口 localDev.js 的实现如下:
const { ApolloServer } = require('apollo-server'); const { typeDefs, resolvers } = require('./index'); const server = new ApolloServer({ typeDefs, resolvers }); server.listen().then(({ url }) => { console.log(`schema ready at ${url}`); });这里使用的是通用版apollo-server包,server.listen()默认监听4000端口并自带 Playground 界面;一旦确认本地可查询,就可以放心地把它部署到云端——因为 Schema 与 Resolver 是从index.js共享出来的同一份定义。
部署到 Google Cloud Functions
1. 准备 gcloud CLI
按 README.md 的步骤,首先安装gcloud命令行工具并完成登录、选择项目(gcloud config set project <your-project-id>)等初始化操作。
2. 编写云函数入口
部署入口 googleCtx.js 使用apollo-server-cloud-functions适配器把 Apollo Server 包装为 Cloud Functions 的 HTTP handler:
const { ApolloServer } = require("apollo-server-cloud-functions"); const { typeDefs, resolvers } = require('./index'); const server = new ApolloServer({ typeDefs, resolvers, playground: true, introspection: true, context: ({ req, res }) => ({ headers: req.headers, req, res, }), }); exports.handler = server.createHandler({ cors: { origin: '*', credentials: true, allowedHeaders: 'Content-Type, Authorization' }, });这段代码值得注意的工程细节:
playground: true与introspection: true允许在线上打开 Playground 与内省查询,方便调试(生产环境可按需关闭);context把 Cloud Functions 的req/res及请求 headers 注入到每个 GraphQL 请求的 context 中,后续 Resolver 可以读取客户端传来的 headers——这与 Hasura 远程 Schema 的 header 转发机制可以很好地配合;createHandler中显式配置了CORS:origin: '*'配合credentials: true,允许任意来源携带Content-Type与Authorization头访问。由于 Hasura GraphQL Engine 通常运行在其他域名/容器中,正确的 CORS 配置是远程 Schema 能被 Engine 正常调用的关键前提。
3. 执行 gcloud 部署命令
在原样板目录下运行(命令取自 README.md):
gcloud functions deploy hello-graphql --entry-point handler --runtime nodejs8 --trigger-http参数含义:
| 参数 | 作用 |
|---|---|
hello-graphql | 云函数名称 |
--entry-point handler | 指定函数入口为googleCtx.js导出的handler |
--runtime nodejs8 | Node.js 运行时版本(样板记录时的版本,请按 GCP 现行支持的运行时调整) |
--trigger-http | 使用 HTTP 触发器,返回可被公网访问的 HTTPS URL |
部署成功后,gcloud会输出函数的触发信息,格式如下(示例):
httpsTrigger: url: https://us-central1-hasura-test.cloudfunctions.net/hello-graphql把httpsTrigger.url的值记录下来——这就是要交给 Hasura 的 GraphQL 端点。
把部署好的服务接入 Hasura GraphQL Engine
远程服务上线后,下一步是把它合并进 GraphQL Engine。官方文档 adding-schema.mdx 提供了 Console、CLI、Metadata API 三种等价方式。
方式一:Console 图形界面
在 Hasura Console 左侧进入Remote Schemas页签,点击Add,填写:
- Remote Schema name:该远程 Schema 的别名,在同一个 GraphQL Engine 实例上必须唯一;
- GraphQL server URL:上一步拿到的
httpsTrigger.url,也可通过环境变量注入; - Headers(可选):可勾选"转发客户端全部 headers",也可追加静态 header 或"header 名-环境变量名"形式的动态 header。
点击Add Remote Schema完成合并,即可在 GraphiQL 页签中查询hello字段。
方式二:CLI 与 Metadata 文件
在metadata/remote_schemas.yaml中添加条目:
- name: hello-graphql definition: url: https://us-central1-hasura-test.cloudfunctions.net/hello-graphql timeout_seconds: 60 forward_client_headers: true然后应用元数据:
hasura metadata apply方式三:Metadata API
向/v1/metadata发送add_remote_schema请求(以 admin 角色为例):
POST /v1/metadata HTTP/1.1 Content-Type: application/json X-Hasura-Role: admin { "type": "add_remote_schema", "args": { "name": "hello-graphql", "definition": { "url": "https://us-central1-hasura-test.cloudfunctions.net/hello-graphql", "forward_client_headers": true, "timeout_seconds": 60 } } }集成注意事项
- 网络可达性:如果 GraphQL Engine 运行在 Docker 容器中,必须确保容器能访问云函数端点;用环境变量注入 URL 时,需在
docker run时通过-e REMOTE_SCHEMA_ENDPOINT=...传入; - 环境变量必须在添加远程 Schema 时就存在且有效,因为 Engine 是在添加时解析并保存 URL/header 值的;
- 当前合并机制的限制(见 remote-schemas.md Caveats):所有合并 Schema 的类型名/节点名必须全局唯一(大小写敏感);同一查询中的所有顶层节点必须来自同一个 GraphQL 服务;远程服务的 Subscription 暂不支持。
实战演进建议
这个hello样板只完成了"最小闭环",在实际项目中通常会沿以下几个方向扩展(均可在本样板结构内完成):
- 扩展 Schema 与 Resolver:在
index.js中加入Mutation、更多 Query 字段,或通过graphql-tools的makeExecutableSchema组合多个模块; - 接入数据源:在 Resolver 中调用 REST API、数据库或第三方 GraphQL 服务,这正是远程 Schema 解决"数据库之外的自定义逻辑"的核心价值;
- 鉴权与 header 透传:利用
googleCtx.js注入的context.headers读取客户端鉴权信息,配合 Hasura 的forward_client_headers与自定义 headers 配置完成端到端安全链路; - 升级运行时与依赖:样板记录的 Node 8.10 /
nodejs8运行时与apollo-server-cloud-functions ^2.4.8、graphql ^0.13.1均属历史版本,部署到当前 GCP 环境时应升级到受支持的 Node 版本并同步升级 Apollo Server 与 graphql 依赖,同时验证 CORS 与 Playground 配置在新版本下的行为。
通过本文的完整流程,你可以把任意自定义 GraphQL 逻辑以 serverless 函数的形式部署到 Google Cloud,再无缝合并进 Hasura 的统一 GraphQL API——这也是官方提供的众多远程 Schema 样板(community/boilerplates/remote-schemas)中,Google Cloud Functions 场景下的标准做法。
【免费下载链接】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),仅供参考