后端API设计【免费下载链接】graphql-yoga Rewrite of a fully-featured GraphQL Server with focus on easy setup, performance great developer experience. The core of Yoga implements WHATWG Fetch API and can run/deploy on any JS environment.项目地址https://gitcode.com/gh_mirrors/gr/graphql-yoga点击查看免费下载导读GraphQL Yoga 的核心卖点之一是“一次编写处处运行”——它基于 WHATWG Fetch API 实现可以部署到 CloudFlare Worker、AWS Lambda、Azure Function、Vercel Function 乃至 Docker 容器等几乎所有 JavaScript 运行时。但“宣称兼容”与“真正兼容”之间隔着一道鸿沟每个平台对请求事件、流式响应、环境变量、包体积的处理方式都不同。本文基于仓库中的 e2e/README.md 及其配套源码完整剖析 GraphQL Yoga 的端到端e2e测试体系它如何用 Pulumi Automation API 在每次 PR 时向真实云环境部署一份本地构建的 Yoga 产物执行GET → GraphiQL与POST → 执行 GraphQL两类冒烟测试再销毁全部资源从而在代码评审阶段就暴露运行时兼容性问题。一、为什么需要真实的端到端测试单元测试覆盖不到的“最后一公里”GraphQL Yoga 的单元测试可以验证解析、执行、插件生命周期等纯逻辑行为但运行时兼容性只能靠真实环境暴露。仓库在 e2e/README.md 中明确了这套体系的目的确保 GraphQL-Yoga 兼容所有流行的运行时runtimes。每个目标平台都有独特的“坑”AWS Lambda的FunctionUrl响应流RESPONSE_STREAM模式与普通 HTTP 返回不同涉及awslambda.streamifyResponse与流式管道处理CloudFlare Worker有 Service Worker 与 ES Modules 两种形态对应cf-worker与cf-modules两个测试计划Azure Function需要 Azure 存储账户承载部署包、WEBSITE_RUN_FROM_PACKAGE设置以及 Linux 动态计划的冷启动Vercel的部署 API 并不完全公开文档化见 CI 注释Docker需要验证打包后的产物在干净容器镜像中的行为。单元测试跑不到这些环境而模拟器如workerd、lambda-local又与真实服务存在差异。因此仓库选择了一条“硬核”路线用真实云资源跑真实请求测完即销毁。从源码结构看这套体系位于 e2e/ 目录包含文件职责e2e/index.tse2e 主入口编排 Pulumi stack 的创建、部署、测试、销毁全流程e2e/utils.ts通用工具getCommitId、waitForEndpoint、assertGraphiQL、assertQuerye2e/types.tsDeploymentConfiguration类型定义每个测试计划的统一契约e2e/tests/各平台的 Pulumi 部署程序与测试逻辑e2e/package.json依赖与启动脚本tsx index.ts二、技术选型Pulumi Automation API 本地构建产物2.1 为什么是 Pulumi Automation API仓库没有用 Pulumi 的声明式 CLI 工作流而是采用Automation API——以编程方式在代码里创建 Pulumi stack、执行up/destroy并读取输出。这样做的好处是显而易见的端到端测试是一次性、短暂存在的环境需要按需创建、测试、销毁完全由测试脚本控制生命周期而不是依赖人工执行 Pulumi CLI 命令。e2e/index.ts 第 31-35 行的核心调用const stack await LocalWorkspace.createOrSelectStack({ projectName: yoga-e2e, stackName: identifier, program: testPlan.program, });LocalWorkspace会在本地运行 Pulumi 程序而不是推送到 Pulumi Cloud 的远程 workspace配合pulumi/pulumi/automation提供的stack.up()、stack.destroy()、stack.refresh()、stack.cancel()等 API 完成全生命周期管理。2.2 关键设计部署“本地构建的 Yoga 产物”README 的 Notes 部分明确了两条重要设计决策每个 PR 都会触发该工作流在代码评审期间就尝试部署并测试真实环境从而发现兼容性问题/运行时问题使用本地构建的 Yoga 版本——所有函数都以构建产物esbuild打包的形式部署而不是发布到 npm 的版本。这意味着 e2e 测试验证的是当前 PR 的代码而不是已发布的旧版本真正做到了“改完即验”。每个测试计划的prerequisites阶段都会先构建对应示例CloudFlarepnpm build产物dist/index.jsAWS Lambdapnpm bundle产物dist/index.js脚本会显式校验文件存在见 e2e/tests/aws-lambda.tsAzure Functionpnpm buildVercelpnpm bundle打包 examples/nextjs-legacy-pages 的产物Dockerpnpm build构建 examples/node-ts 的产物2.3 测试计划注册表与统一契约e2e/index.ts 第 11-18 行维护了一张测试计划注册表通过环境变量TEST_PLAN_NAME选择const AVAILABLE_TEST_PLANS: Recordstring, DeploymentConfigurationany | undefined { cf-worker: cloudFlareDeployment, cf-modules: cfModulesDeployment, azure-function: azureFunctionDeployment, aws-lambda: awsLambdaDeployment, vercel-function: vercelDeployment, docker-node: dockerDeployment(node:24), };每个测试计划都实现统一的 DeploymentConfiguration 契约包含四个可选/必选阶段export type DeploymentConfigurationTProgramOutput {} { prerequisites?: (stack: Stack) Promisevoid; // 安装插件、构建产物 config?: (stack: Stack) Promisevoid; // 写入云厂商凭据等 Pulumi 配置 program: () Promise{ ... }; // Pulumi 资源定义返回输出 test: (output: { ... }) Promisevoid; // 对部署后的端点执行断言 };stack 名称由yoga-${TEST_PLAN_NAME}-e2e-${COMMIT_ID}构成见 e2e/index.ts其中COMMIT_ID优先取环境变量否则回退到git rev-parse HEAD见 e2e/utils.ts。这样每次提交对应唯一的 stack便于定位和清理。三、主流程详解从部署到销毁的五步曲e2e/index.ts 的main()函数完整展示了执行流程可分五个阶段阶段一创建/选择 Stack根据TEST_PLAN_NAME取出对应的DeploymentConfiguration用LocalWorkspace.createOrSelectStack创建或选择 stack。若计划不存在直接抛出Test plan xxx not found。阶段二前置准备与配置if (testPlan.prerequisites) { await testPlan.prerequisites(stack); // 安装 Pulumi 插件 构建/打包示例产物 } if (testPlan.config) { await testPlan.config(stack); // 写入云厂商凭据等 Pulumi 配置 }例如 Azure 测试计划通过stack.setConfig写入azure-native:clientId、clientSecret、tenantId、subscriptionId、locationwestus五项配置见 e2e/tests/azure-function.tsAWS 则写入aws:accessKey、secretKey、region、allowedAccountIds见 e2e/tests/aws-lambda.ts。凭据全部来自 CI secrets不硬编码在仓库中。阶段三处理僵尸 Stack 与状态刷新源码对“上次任务残留”做了防御性处理若检测到 stack 状态为in-progress或not-started可能是上次失败遗留的 zombie 任务会先调用stack.cancel()取消然后强制stack.refresh()刷新状态本地非 CI运行时也总是 refresh以确保拿到最新改动见 e2e/index.ts。阶段四部署与冒烟测试const upRes await stack.up({ onOutput: console.log }); await testPlan.test(upRes.outputs);stack.up()执行 Pulumi 程序创建真实资源返回的输出如functionUrl、workerUrl、endpoint随后传给测试函数。阶段五销毁清理finally 块无论测试成功还是失败finally块都会执行清理。默认调用stack.destroy()删除所有资源若设置了环境变量KEEP则保留资源供人工调试——源码注释特别提醒使用KEEP后务必再跑一次不带KEEP的销毁或手动删除避免遗留云资源产生费用见 e2e/index.ts。四、冒烟测试协议GET 与 POST 两类断言README 描述的核心冒烟测试为GET → GraphiQL和POST → 执行 GraphQL二者在 e2e/utils.ts 中实现为两个可复用的断言函数。4.1assertGraphiQL验证 GraphiQL 界面const response await fetch(endpoint, { method: GET, headers: { accept: text/html }, }); const html await response.text(); if (response.status ! 200) { /* 抛出异常 */ } if (!html.includes(titleYoga GraphiQL/title)) { throw new Error(Failed to locate GraphiQL: failed to find signs for GraphiQL HTML); }它通过检查响应 HTML 中是否包含titleYoga GraphiQL/title来判断 GraphiQL 是否正确渲染——这是 Yoga 默认内置 GraphiQL 的标志性产物验证了 Yoga 的 HTML 响应与静态资源在各平台上正常。4.2assertQuery执行真实 GraphQL 操作默认发送的查询是query { greetings }该查询对应示例 schema 中的Query.greetings字段其返回值在 examples/node-ts/src/yoga.ts 等示例中统一实现为This is thegreetingsfield of the rootQuerytype断言逻辑层层递进见 e2e/utils.tsPOST请求Content-Type: application/json携带{ query, variables }状态码必须为 200响应 JSON 中不得存在errors字段若使用默认查询data.greetings必须精确等于上述字符串。任何一个环节不满足都会抛出带详细上下文状态码、响应体、错误列表的异常便于定位是“路由问题”“schema 问题”还是“执行结果不符”。4.3waitForEndpoint处理冷启动与传播延迟云函数部署后有冷启动和 DNS/路由传播延迟因此测试前会调用waitForEndpoint轮询端点。其实现要点见 e2e/utils.ts默认 5 次重试、每次间隔 10 秒要求GET返回 200且响应体不得包含 Vercel防止误命中 Vercel 的占位/错误页面失败时打印警告并等待后重试全部失败则抛出Failed to connect to endpoint。五、各平台测试计划实现剖析5.1 CloudFlare WorkerService Worker 与 ES Modules 双形态e2e/tests/cf-worker.ts 与 e2e/tests/cf-modules.ts 都委托给工厂函数 createCFDeployment区别仅在于isModule参数export const cloudFlareDeployment createCFDeployment(service-worker); // 经典 Service Worker 形态 export const cfModulesDeployment createCFDeployment(cloudflare-modules, true); // ES Modules 形态部署程序使用cf.WorkerScript上传dist/index.js并注入两个 plain text 绑定GRAPHQL_ROUTE/${stackName}与DEBUGtrue随后用cf.WorkerRoute将e2e.graphql.yoga/${stackName}路由绑定到该 Worker见 e2e/tests/create-cf-deployment.ts。测试则对https://e2e.graphql.yoga/${stackName}依次执行等待、GraphiQL 断言与 GraphQL 查询断言。从仓库结构看这两个计划分别对应示例 examples/cloudflare-advanced 与 examples/cloudflare-modules验证了 Yoga 在两种 Worker 形态下的 Fetch API 适配。5.2 AWS Lambda函数 URL 响应流RESPONSE_STREAMe2e/tests/aws-lambda.ts 的部署程序非常具有代表性创建 IAM Role 与 RolePolicy授权logs:CreateLogGroup、logs:CreateLogStream、logs:PutLogEvents用aws.lambda.Function创建nodejs20.x运行时、handler 为index.handler的 Lambda关键点aws.lambda.FunctionUrl使用invokeMode: RESPONSE_STREAM开启响应流模式见 e2e/tests/aws-lambda.ts并附加lambda:InvokeFunctionUrl权限、functionUrlAuthType: NONE匿名访问。响应流模式对应用层有直接影响对应示例 examples/aws-lambda/lambda/graphql.ts 中handler 使用awslambda.streamifyResponse包装通过pipeline(response.body, res)将 Yoga 的 Fetch 响应体管道到 Lambda 响应流。测试对/graphql端点并行执行assertQuery与assertGraphiQL并用Promise.allSettled汇总所有断言失败见 e2e/tests/aws-lambda.ts。5.3 Azure Function存储账户 Linux 动态计划e2e/tests/azure-function.ts 是资源最重的一个计划部署了ResourceGroup资源组StorageAccountStandard_LRS、StorageV2与 Blob 容器/Blob承载部署包FileArchive打包 examples/azure-function 目录AppServicePlanLinux、Y1动态计划WebAppkind: functionapp关键配置FUNCTIONS_EXTENSION_VERSION~4、FUNCTIONS_WORKER_RUNTIMEnode、WEBSITE_RUN_FROM_PACKAGEblob URL、linuxFxVersion: node|20见 e2e/tests/azure-function.ts。Blob 的 SAS 签名 URL 通过signedBlobReadUrl生成https协议、2030 年过期、只读权限。源码中留有一段面向维护者的注释更新该计划时需同时核对linuxFxVersion与FUNCTIONS_EXTENSION_VERSION的版本匹配见 e2e/tests/azure-function.ts。测试对https://${app.defaultHostName}/api/yoga对应示例中graphqlEndpoint: /api/yoga的路由执行 GraphiQL 与查询断言。5.4 Vercel自定义 Pulumi 动态 ProviderVercel 的部署较特殊——仓库没有官方 Pulumi provider而是用pulumi.dynamic.ResourceProvider自己实现了 Vercel API 客户端见 e2e/tests/vercel.ts。VercelProvider直接调用 Vercel REST APIPOST /v13/deployments创建部署提交文件数组/api/graphql.js为打包后的 examples/nextjs-legacy-pages 产物以及指定engines.node: ^18.0.0的 package.json、函数配置api/graphql.js256MB 内存、最大 5 秒执行时长、项目设置DELETE /v13/deployments/{id}销毁部署。测试对https://${deployment.url}/api/graphql执行等待、GraphiQL 与查询断言。值得注意的是CI 中该计划目前被注释禁用注释写明原因是“vercel API is not actually documented”Vercel API 并未真正文档化——这是仓库对事实边界的一种诚实处理。5.5 Docker 容器验证打包产物在干净镜像中运行e2e/tests/docker.ts 由工厂函数dockerDeployment(image)生成默认注册表项为dockerDeployment(node:24)docker.RemoteImage拉取指定 Node 镜像keepLocally: truedocker.Container挂载 examples/node-ts/dist 构建产物到容器/app工作目录设为/app执行node index.js暴露容器内 4000 端口由于 provider 会分配随机临时端口端点通过container.ports.apply(...)动态解析为http://127.0.0.1:{external}/graphql见 e2e/tests/docker.ts。该计划验证的是本地 esbuild/tsc 构建出的产物在未预装任何额外依赖的干净 Node 镜像中能否独立启动并正确响应 GraphQL 请求——这正是发布产物可移植性的直接证据。六、CI 集成每次 PR 的真实环境验证e2e 测试被接入 .github/workflows/ci.yml 的e2ejob约第 251-356 行其运行策略包括矩阵并行matrix.plan覆盖aws-lambda、cf-worker、cf-modules、docker-nodeazure-function与vercel-function因资源或 API 文档原因被注释禁用fail-fast: false确保单个平台失败不阻塞其他平台触发条件仅当pull_request事件且 PR 来源仓库为上游仓库排除 fork PR、dependabot、copilot-swe-agent 等 bot以保护 secrets前置步骤安装 pnpm、Node、依赖与pnpm build全量构建再安装 Pulumi CLI随后通过nick-fields/retry重试包装执行cd e2e pnpm start超时 10 分钟、最多 10 次尝试、间隔 30 秒以应对云资源创建的偶发超时强制清理使用if: always()的独立步骤以ENSURE_DELETION: 1再次运行pnpm start——即使主测试步骤失败也会执行。ENSURE_DELETION的语义在 e2e/index.ts 中体现该模式下跳过部署与测试只检查并清理已存在的 stack若状态为in-progress/not-started则先cancel确保僵尸资源被回收。这一设计保证了“测试成功要销毁测试失败也要销毁”避免云资源泄漏。CI 中注入的 secrets 涵盖了五个平台所需的全部凭据CloudFlare 的API_TOKEN、ACCOUNT_ID、ZONE_IDAzure 的TENANT_ID、CLIENT_SECRET、CLIENT_ID、SUBSCRIPTION_IDAWS 的ACCESS_KEY、SECRET_KEY、ACCOUNT_ID、REGION以及PULUMI_ACCESS_TOKEN与VERCEL_AUTH_TOKEN。七、环境变量速查表综合 e2e/index.ts 与各测试计划源码整套体系涉及的环境变量如下环境变量用途来源TEST_PLAN_NAME选择测试计划cf-worker/cf-modules/azure-function/aws-lambda/vercel-function/docker-node必填缺失即抛错e2e/index.tsCOMMIT_IDstack 名称标识未设置时回退到git rev-parse HEADe2e/utils.tsENSURE_DELETION置 1 时只清理不部署e2e/index.tsKEEP置位时保留已部署资源慎用需手动清理e2e/index.tsCLOUDFLARE_API_TOKEN/CLOUDFLARE_ACCOUNT_ID/CLOUDFLARE_ZONE_IDCloudFlare 凭据与路由 zonee2e/tests/create-cf-deployment.tsAWS_ACCESS_KEY/AWS_SECRET_KEY/AWS_REGION/AWS_ACCOUNT_IDAWS 凭据与账户白名单e2e/tests/aws-lambda.tsAZURE_CLIENT_ID/AZURE_CLIENT_SECRET/AZURE_TENANT_ID/AZURE_SUBSCRIPTION_IDAzure 服务主体凭据e2e/tests/azure-function.tsVERCEL_AUTH_TOKEN/VERCEL_TEAM_IDVercel API 凭据TEAM_ID可选e2e/tests/vercel.ts八、本地运行与注意事项参照 CI 的调用方式.github/workflows/ci.yml本地运行一个测试计划的基本步骤是# 1. 安装依赖并全量构建确保使用本地 Yoga 产物 pnpm i pnpm build # 2. 准备目标平台的云厂商凭据export 对应的环境变量 # 3. 在 e2e 目录启动测试需先安装 Pulumi CLI cd e2e TEST_PLAN_NAMEcf-worker COMMIT_ID$(git rev-parse --short HEAD) pnpm start需要特别说明的注意事项必须使用本地构建产物所有测试计划都会在prerequisites阶段重新构建对应示例因此仓库根目录先执行pnpm build是 CI 的标准前提凭据必须齐备env()工具函数对缺失的环境变量直接抛错见 e2e/utils.ts配置阶段也会因缺失 secrets 而失败慎用KEEP源码注释明确提示保留资源后需再次运行销毁或手动清理见 e2e/index.ts注意平台差异Azure 计划销毁耗时较长约 10 个较重资源销毁额外耗时 30-60 秒见 e2e/index.tsVercel 计划因 API 未文档化而处于禁用状态见 .github/workflows/ci.yml 中的矩阵注释。九、从这套体系可以学到什么从仓库源码e2e/index.ts、e2e/utils.ts、e2e/types.ts、e2e/tests/可以提炼出几条可复用的工程实践用 IaC 管理短暂测试环境Pulumi Automation API 让“创建-测试-销毁”成为代码可控的原子流程stack 名称编码 commit 与计划名天然支持并行与清理统一契约 工厂函数DeploymentConfiguration类型约束每个平台的四个阶段CloudFlare 双形态与 Docker 多镜像通过工厂函数复用逻辑避免重复代码协议级冒烟测试GET → GraphiQL校验titleYoga GraphiQL/title与POST → 执行 GraphQL校验状态码、无 errors、精确返回体是覆盖“路由 执行 响应”的最小充分集合防御性资源管理zombie stack 检测cancelrefresh、ENSURE_DELETION独立清理步骤、if: always()兜底共同保证云资源不泄漏诚实标注边界Vercel 计划因 API 未文档化被显式禁用而不是强行测试——这对维护者可读性至关重要。这套体系直接服务于 GraphQL Yoga 的跨运行时承诺无论部署目标是 CloudFlare Worker、AWS Lambda、Azure Function、Vercel 还是 Docker 容器每次代码变更都会在真实环境中经受 GET 与 POST 双冒烟测试的检验任何运行时兼容性问题都会在代码评审阶段被尽早暴露而非等用户在生产环境中踩坑。赞分享后端API设计【免费下载链接】graphql-yoga Rewrite of a fully-featured GraphQL Server with focus on easy setup, performance great developer experience. The core of Yoga implements WHATWG Fetch API and can run/deploy on any JS environment.项目地址https://gitcode.com/gh_mirrors/gr/graphql-yoga点击查看免费下载相关推荐Civitai Auth Hub 部署环境端到端冒烟测试框架基于 Playwright 的登录中心验证方案Civitai Auth Hub 部署环境端到端冒烟测试框架基于 Playwright 的登录中心验证方案 导读 本文讲解 Civitai 登录中心auth后端前端AI 应用Automatisch E2E 测试环境搭建与运行指南基于 Playwright 的端到端测试实战Automatisch E2E 测试环境搭建与运行指南基于 Playwright 的端到端测试实战 本指南以 Automatisch 仓库中的 package工作流自动化后端前端低代码任务调度Composio E2E 测试体系实战基于 Docker 的多运行时端到端测试框架解析Composio E2E 测试体系实战基于 Docker 的多运行时端到端测试框架解析 导读 ts/e2e tests/ 是 Composio 仓库中专为 人工智能AI Agent工具调用MCP 服务MCP Clients上一篇让API文档脱颖而出Jazzy文档SEO优化指南下一篇ActiveScan 2.0 重写记从 Jython 到 Java 的架构演进与性能优化之路创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
