- 后端
- GraphQL
- API设计
【免费下载链接】type-graphql
Create GraphQL schema and resolvers with TypeScript, using classes and decorators!
导读
本指南围绕 TypeGraphQL 官方文档中 "Azure Functions Integration" 一章展开,手把手讲解如何在 Microsoft Azure Functions 的 HTTP Trigger(Http 触发器)中接入 TypeGraphQL:先用buildSchemaSync基于 Resolver 类生成可执行 GraphQL Schema,再把 Schema 交给 Apollo Server,最后通过startServerAndCreateHandler导出为 Azure Function 的默认处理器。读完本文,你将掌握入口函数index.ts、函数绑定配置文件function.json的完整写法,以及适合长期维护的目录组织方式,并能理解emitSchemaFile、container、validate等构建选项在 serverless 场景下的真实作用。
一、集成原理与两个关键步骤
在 Azure Functions 这类 serverless 环境中运行 TypeGraphQL,本质上与常规 Node.js 服务部署没有区别,核心只在于两步:
- 生成 GraphQL Schema:把通过
@ObjectType、@Resolver、@Query、@Mutation等装饰器声明的类元数据,编译成一个可执行的GraphQLSchema实例; - 把 Schema 交给 Apollo Server:让 Apollo Server 承担查询解析、验证与执行的职责,再把它包装成 Azure Functions 识别的 HTTP 处理器。
该章节是 2.0.0-rc 系列文档的版本化快照,位于 website/versioned_docs/version-2.0.0-rc.2/azure-functions.md,当前仓库主文档对应 docs/azure-functions.md,两者内容一致,可对照阅读。
前置依赖
参照仓库根目录 package.json 的依赖声明(当前仓库版本为2.0.0-rc.4),除 TypeGraphQL 本身外,一个可运行的集成方案通常需要以下包:
| 依赖 | 用途 |
|---|---|
reflect-metadata | 装饰器元数据反射的运行时依赖,TypeGraphQL 必须最先 import |
@apollo/server | Apollo Server 4/5 核心,承载 Schema 执行(仓库 devDependencies 使用^5.2.0) |
@as-integrations/azure-functions | Apollo Server 官方 Azure Functions 集成适配器,提供startServerAndCreateHandler |
typedi | 依赖注入容器(仓库 devDependencies 使用^0.10.0),用于container选项 |
graphql | GraphQL 核心库(TypeGraphQL 的 peerDependency 要求^16.12.0) |
class-validator | 可选的参数校验库(peerDependency>=0.14.3,validate: true时使用) |
TypeGraphQL 要求 Node.js 版本>= 20.11.1(见 package.json 的engines字段),Azure Functions 的 Node 运行时需满足该前提。
二、入口函数实现:完整的 index.ts
文档给出了一个可直接套用的 Azure Function 入口实现(即函数根目录下的index.ts),核心思路是在模块顶层同步构建 Schema,因此每个函数实例在加载时只构建一次:
// index.ts import "reflect-metadata"; import path from "path"; import { ApolloServer } from "@apollo/server"; import { startServerAndCreateHandler } from "@as-integrations/azure-functions"; import { buildSchemaSync } from "type-graphql"; import { Container } from "typedi"; import { GraphQLFormattedError } from "graphql"; import { UserResolver } from "YOUR_IMPORT_PATH"; // TypeGraphQL Resolver import { AccountResolver } from "YOUR_IMPORT_PATH"; // TypeGraphQL Resolver // Bundle resolvers to build the schema const schema = buildSchemaSync({ // Include resolvers you'd like to expose to the API // Deployment to Azure functions might fail if // you include too much resolvers (means your app is too big) resolvers: [ UserResolver, AccountResolver, // your other resolvers ], // Only build the GraphQL schema locally // The resulting schema.graphql will be generated to the following path: // Path: /YOUR_PROJECT/src/schema.graphql emitSchemaFile: process.env.NODE_ENV === "local" ? path.resolve("./src/schema.graphql") : false, container: Container, validate: true, }); // Add schema into Apollo Server const server = new ApolloServer({ // include your schema schema, // only allow introspection in non-prod environments introspection: process.env.NODE_ENV !== "production", // you can handle errors in your own styles formatError: (err: GraphQLFormattedError) => err, }); // Start the server(less handler/function) export default startServerAndCreateHandler(server);下面按代码块逐一拆解关键点。
2.1import "reflect-metadata"必须放在首位
TypeGraphQL 依赖装饰器元数据(emitDecoratorMetadata)驱动类型推断,reflect-metadata必须在任何装饰器使用前完成 polyfill(仓库在tests/functional/errors/metadata-polyfill.ts中也对该依赖做了专项测试保障)。import "reflect-metadata"放在文件第一行是官方推荐的稳妥写法。
2.2buildSchemaSync:同步构建 Schema
与常规服务端常用的buildSchema(异步,见 src/utils/buildSchema.ts)不同,这里使用buildSchemaSync,它在模块加载期间同步完成 Schema 生成(src/utils/buildSchema.ts),无需等待 Promise,因此可以直接把结果赋给模块级常量。从源码结构看,两种 API 内部都会调用SchemaGenerator.generateFromMetadata,区别仅在于是否在构建后同步写出 SDL 文件。
2.3resolvers:显式声明暴露的 Resolver
resolvers数组决定哪些 Resolver 类被注册进 Schema(对应 src/utils/buildSchema.ts 中BuildSchemaOptions.resolvers的NonEmptyArray<Function>类型约束——不允许空数组,源码loadResolvers在空数组时会直接抛错)。
文档在此特别提醒:Azure Functions 部署包过大可能导致部署失败,因此应只包含真正需要对外暴露的 Resolver,把无关 Resolver 挡在 Schema 之外,这既是功能边界,也是控制函数体积的手段。
2.4emitSchemaFile:仅本地生成 SDL 文件
emitSchemaFile: process.env.NODE_ENV === "local" ? path.resolve("./src/schema.graphql") : false,这是一个典型的"环境区分"写法:
- 本地开发(
NODE_ENV === "local"):把生成的schema.graphql写到src/目录,便于用 GraphQL 工具(如 Playground、IDE 智能提示、代码生成器)消费; - 非本地(生产):传
false,跳过文件写出,避免在函数运行期做无谓的 IO。
从源码看,emitSchemaFile选项实际支持string | boolean | 配置对象三种形态(src/utils/buildSchema.ts):
true:输出到默认路径,即process.cwd()下的schema.graphql(见getEmitSchemaDefinitionFileOptions的默认逻辑,src/utils/buildSchema.ts);string:按给定路径输出;- 配置对象:可额外控制打印选项(例如
sortedSchema)。
写出 SDL 的底层实现在 src/utils/emitSchemaDefinitionFile.ts:默认会对 Schema 做字典序排序(lexicographicSortSchema)并在文件头部追加一段 "THIS FILE WAS GENERATED BY TYPE-GRAPHQL" 的警告注释,提示该文件为生成产物、不要手改。
2.5container:接入依赖注入容器
container: Container, // 来自 typed-i 的 Container对应 src/schema/build-context.ts 的container?: ContainerType | ContainerGetter<any>,用于让 Resolver、Service 等类通过 IoC 容器实例化,实现依赖注入。仓库中using-container、using-scoped-container等示例(见 examples/using-container)展示了容器接入的完整姿势;若不传,TypeGraphQL 默认使用无容器模式直接实例化类。
2.6validate: true:启用参数自动校验
validate对应 src/schema/build-context.ts 的ValidateSettings = boolean | ValidatorOptions(src/schema/build-context.ts)。传true表示启用基于class-validator装饰器(@IsEmail、@MinLength等)的入参校验;也可以直接传入一个ValidatorOptions对象做精细化配置。这是文档推荐在生产级 API 中开启的选项。
三、Apollo Server 配置要点
const server = new ApolloServer({ schema, introspection: process.env.NODE_ENV !== "production", formatError: (err: GraphQLFormattedError) => err, });schema:把 TypeGraphQL 构建出的可执行 Schema 注入 Apollo Server;introspection:仅非生产环境开启内省(GraphQL Playground、Apollo Sandbox 依赖它);formatError:按需定制错误输出格式(脱敏、附加上下文、统一结构等),示例中是透传原样返回。
最后export default startServerAndCreateHandler(server)把 Apollo Server 包装成 Azure Functions 的 HTTP 处理器并作为默认导出,函数运行时即会调用该处理器响应请求。
四、function.json:HTTP 触发器的绑定配置
每个 Azure Function 都需要一个同名的function.json配置文件来描述绑定(bindings)。文档给出的 GraphQL 端点配置如下:
// function.json { "bindings": [ { "authLevel": "anonymous", "type": "httpTrigger", "direction": "in", "name": "req", "route": "graphql", "methods": ["get", "post", "options"] }, { "type": "http", "direction": "out", "name": "$return" } ], "scriptFile": "../dist/handler-graphql/index.js" }逐字段说明:
| 字段 | 取值 | 含义 |
|---|---|---|
authLevel | "anonymous" | 匿名访问即可触发,无需函数级鉴权(业务鉴权可交给 TypeGraphQL 的authChecker/@Authorized机制) |
type | "httpTrigger"/"http" | 入站为 HTTP 触发器,出站为 HTTP 响应 |
direction | "in"/"out" | 绑定方向 |
name | "req"/"$return" | 入站请求对象名;出站用$return表示直接以返回值作为响应 |
route | "graphql" | 路由路径,最终端点形如https://<your-app>.azurewebsites.net/api/graphql |
methods | ["get", "post", "options"] | 允许的 HTTP 方法:GET/POST用于查询执行,OPTIONS用于 CORS 预检 |
scriptFile | "../dist/handler-graphql/index.js" | 指向编译后的 JS 入口(TypeScript 源码需先编译到dist/,与下文目录结构对应) |
注意:scriptFile是相对函数目录的路径,因此当函数放在handlers/handler-graphql/下、编译产物在dist/handler-graphql/时,需要写成"../dist/handler-graphql/index.js"。
五、推荐目录结构:函数与业务代码分离
文档强调:为了代码库的可维护性,不要把 Azure Functions 处理器与 GraphQL Resolver 混放在一起,而是各归其位。推荐的布局如下:
/YOUR_PROJECT /handlers /handler-graphql index.ts function.json /handler-SOME-OTHER-FUNCTION-1 index.ts function.json /handler-SOME-OTHER-FUNCTION-2 index.ts function.json /src /resolvers user.resolver.ts account.resolver.ts /services user.service.ts account.service.ts package.json host.json .eslintrc.js .prettierrc .eslintignore .prettierignore etc etc etc...handlers/:一个子目录对应一个 Azure Function,各自包含index.ts与function.json,职责单一、便于后续新增函数;src/resolvers/、src/services/:GraphQL 业务代码(Resolver、Service、Type 等)独立成层,与 serverless 入口解耦,可被本地服务、测试或其他入口复用;- 工程配置(
package.json、host.json、ESLint/Prettier 配置等)置于根目录。
这种分层与仓库中 examples 里各个示例的组织风格一致:业务逻辑集中在 resolver/type/service 文件,入口只做组装。
六、serverless 场景的注意事项与参考对照
6.1 Schema 构建成本的取舍
从 src/schema/schema-generator.ts 的实现看,每次generateFromMetadata都会克隆元数据存储、遍历 Resolver 构建类型信息,并在skipCheck为false(默认)时额外执行一次 introspection 查询来校验 Schema 正确性——这是一份不轻的冷启动开销。本文方案把buildSchemaSync放在模块顶层,让同一函数实例只构建一次;但函数实例被回收后的冷启动仍会重建。
如果对冷启动延迟敏感,可以参考仓库中 AWS Lambda 集成文档(docs/aws-lambda.md)给出的缓存模式:用模块级变量配合??=条件赋值,把 Schema 与 Server 缓存到实例生命周期内,避免每次请求重复构建:
let cachedSchema: GraphQLSchema | null = null; let cachedServer: ApolloServer | null = null; export const handler = async (event, context, callback) => { cachedSchema ??= await buildSchema({ resolvers: [RecipeResolver] }); cachedServer ??= new ApolloServer({ schema: cachedSchema }); // ... };两种方案的取舍:文档版代码更简洁、结构更直观;缓存版在多次冷启动场景下可省去重复构建。可按团队偏好选择,但无论哪种,Schema 构建都只应发生一次。
6.2 部署包大小
文档明确提醒:"Deployment to Azure functions might fail if you include too much resolvers (means your app is too big)"。Azure Functions(特别是消费计划)对部署包大小有限制,因此:
resolvers数组只注册真正对外暴露的 Resolver;- 利用
emitSchemaFile的本地生成能力把schema.graphql纳入版本管理/CI 检查,及时发现类型错误; - 必要时按领域拆分多个函数,各函数只携带自己需要的 Resolver 集合,配合上面的多
handlers目录结构落地。
6.3 本地开发验证
本地运行 Azure Functions 工具时,NODE_ENV=local会让emitSchemaFile生效,把最新 Schema 写入src/schema.graphql;之后既可用 Apollo Sandbox / GraphQL Playground 直连本地端点调试,也可用该 SDL 文件做客户端代码生成。生产环境传false则完全跳过写盘,避免无谓 IO。
七、小结
把 TypeGraphQL 接到 Azure Functions 上,只需三步:用buildSchemaSync基于 Resolver 生成 Schema → 注入 Apollo Server → 用startServerAndCreateHandler导出为函数处理器,再配一份声明了httpTrigger绑定与编译产物入口的function.json。官方推荐把函数处理器与业务 Resolver 分层存放,并借助emitSchemaFile仅在本地产出 SDL、借助container/validate开启依赖注入与入参校验。以上配置细节与实现依据均可在仓库 docs/azure-functions.md、src/utils/buildSchema.ts 与 src/schema/build-context.ts 中进一步查阅验证。
- 后端
- GraphQL
- API设计
【免费下载链接】type-graphql
Create GraphQL schema and resolvers with TypeScript, using classes and decorators!
相关推荐
TypeGraphQL Azure Functions部署:Serverless GraphQL实践
TypeGraphQL Azure Functions部署:Serverless GraphQL实践 在Serverless架构兴起的今天,开发者面临着如何将T
后端GraphQLAPI设计构建Serverless API:gh_mirrors/cad/caddy与Azure Functions集成
构建Serverless API:gh_mirrors/cad/caddy与Azure Functions集成 你是否在寻找一种简单高效的方式将Caddy服务器
后端API网关网络TypingMind企业版定制方案:团队协作与知识库集成最佳实践
TypingMind企业版定制方案:团队协作与知识库集成最佳实践 TypingMind企业版是专为团队协作设计的AI聊天界面解决方案,为企业提供完整的自定义品牌
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考