生成到程序化 emit)
后端GraphQLAPI設(shè)計(jì)【免費(fèi)下載鏈接】type-graphqlCreate GraphQL schema and resolvers with TypeScript, using classes and decorators!項(xiàng)目地址https://gitcode.com/gh_mirrors/ty/type-graphql點(diǎn)擊查看免費(fèi)下載TypeGraphQL 的核心能力是通過(guò) TypeScript 類(lèi)和裝飾器直接生成 GraphQL schema而無(wú)需手寫(xiě) SDL。但在很多真實(shí)場(chǎng)景下我們?nèi)匀恍枰?schema 打印成schema.graphql舊版本為schema.gql文本文件——比如供 GraphQL 生態(tài)中的客戶(hù)端工具做查詢(xún)自動(dòng)補(bǔ)全與校驗(yàn)、作為回歸檢測(cè)的快照、或者讓團(tuán)隊(duì)成員直接閱讀 SDL 來(lái)探索 API。本文以 TypeGraphQL 官方文檔為基礎(chǔ)結(jié)合倉(cāng)庫(kù)源碼與測(cè)試用例系統(tǒng)講解兩種輸出 schema 定義文件的完整方案buildSchema的emitSchemaFile自動(dòng)生成以及emitSchemaDefinitionFile/emitSchemaDefinitionFileSync的程序化生成并深入剖析底層實(shí)現(xiàn)細(xì)節(jié)。為什么要輸出 Schema SDL 文件TypeGraphQL 的主打特性是只用類(lèi)與裝飾器建 schema因此生成的 schema 對(duì)象通常只存在于運(yùn)行時(shí)內(nèi)存中。但以下場(chǎng)景需要它被持久化為 SDL 文本文件客戶(hù)端工具鏈GraphQL 生態(tài)中的很多工具需要 SDL 文件來(lái)完成客戶(hù)端查詢(xún)的自動(dòng)補(bǔ)全與校驗(yàn)Schema 回歸檢測(cè)把 SDL 文件當(dāng)作快照snapshot通過(guò) diff 感知 schema 的意外變更API 探索相比閱讀復(fù)雜的 TypeGraphQL 應(yīng)用代碼、或在 GraphiQL / GraphQL Playground 中反復(fù)點(diǎn)擊直接閱讀 SDL 文件往往更直觀(guān)高效。TypeGraphQL 為此提供了兩種生成 schema 定義文件的方式下文分別展開(kāi)。值得注意的是0.17.0 時(shí)代默認(rèn)輸出的文件名是schema.gql而當(dāng)前倉(cāng)庫(kù)版本對(duì)應(yīng) docs/emit-schema.md中默認(rèn)文件名已統(tǒng)一為schema.graphql下文以當(dāng)前倉(cāng)庫(kù)行為為準(zhǔn)。方式一通過(guò) buildSchema 的 emitSchemaFile 選項(xiàng)自動(dòng)生成最省事的方式是在調(diào)用buildSchema時(shí)傳入emitSchemaFile選項(xiàng)讓 TypeGraphQL 在每次構(gòu)建 schema 時(shí)自動(dòng)把定義寫(xiě)入文件。該選項(xiàng)支持三種形態(tài)布爾值、字符串路徑、以及配置對(duì)象。const schema await buildSchema({ resolvers: [ExampleResolver], // 自動(dòng)在項(xiàng)目工作目錄下創(chuàng)建 schema.graphql 文件 emitSchemaFile: true, // 或者指定文件寫(xiě)入路徑 emitSchemaFile: path.resolve(__dirname, __snapshots__/schema/schema.graphql), // 或者傳入配置對(duì)象精細(xì)化控制輸出 emitSchemaFile: { path: __dirname /schema.graphql, sortedSchema: false, // 默認(rèn)情況下輸出的 schema 會(huì)按字母序排序 }, });三種傳參形態(tài)的語(yǔ)義從 src/utils/buildSchema.ts 的getEmitSchemaDefinitionFileOptions實(shí)現(xiàn)可以精確還原三種形態(tài)的處理邏輯emitSchemaFile: true使用默認(rèn)路徑path.resolve(process.cwd(), schema.graphql)即當(dāng)前進(jìn)程工作目錄process.cwd()下的schema.graphqlemitSchemaFile: 路徑字符串把字符串直接當(dāng)作完整的目標(biāo)文件路徑包含文件名示例中的__snapshots__/schema/schema.graphql即屬此類(lèi)emitSchemaFile: { ... }配置對(duì)象對(duì)象類(lèi)型為EmitSchemaFileOptions即{ path?: string } PartialPrintSchemaOptions。其中path缺省時(shí)回落為默認(rèn)路徑其余屬性即PrintSchemaOptions的字段會(huì)與默認(rèn)值做淺合并{ ...defaultPrintSchemaOptions, ...options }。PrintSchemaOptions控制 schema 輸出的格式PrintSchemaOptions是控制輸出格式的配置接口定義于 src/utils/emitSchemaDefinitionFile.tsexport interface PrintSchemaOptions { sortedSchema: boolean; } export const defaultPrintSchemaOptions: PrintSchemaOptions { sortedSchema: true, };sortedSchema默認(rèn)true決定打印前是否對(duì) schema 做字典序排序。排序通過(guò)graphql-js的lexicographicSortSchema實(shí)現(xiàn)見(jiàn)同文件getSchemaFileContent使類(lèi)型、字段按字母序穩(wěn)定排列利于生成 diff 友好的快照文件設(shè)為false則保留 schema 構(gòu)建時(shí)的原始定義順序。0.17.0 舊版文檔中展示的commentDescriptions: true選項(xiàng)把...描述輸出為#注釋形式在舊版PrintSchemaOptions中存在當(dāng)前倉(cāng)庫(kù)版本的選項(xiàng)接口已收斂為sortedSchema一個(gè)字段使用時(shí)以當(dāng)前安裝版本導(dǎo)出的類(lèi)型為準(zhǔn)。自動(dòng)生成的文件頭部警告通過(guò)emitSchemaFile或emitSchemaDefinitionFile生成的文件并非純 SDL而是帶有一段固定的生成警告頭generatedSchemaWarning定義于 src/utils/emitSchemaDefinitionFile.ts# ----------------------------------------------- # !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!! # !!! DO NOT MODIFY THIS FILE BY YOURSELF !!! # -----------------------------------------------這提醒開(kāi)發(fā)者該文件是構(gòu)建產(chǎn)物、不應(yīng)手工修改。測(cè)試 tests/functional/emit-schema-sdl.ts 中的checkSchemaSDL也明確斷言生成內(nèi)容必須包含THIS FILE WAS GENERATED字樣。路徑不存在時(shí)自動(dòng)創(chuàng)建目錄emitSchemaFile指向的目錄不存在時(shí)TypeGraphQL 不會(huì)報(bào)錯(cuò)而是自動(dòng)遞歸創(chuàng)建目錄。其底層由 src/helpers/filesystem.ts 的outputFile/outputFileSync完成先嘗試直接寫(xiě)文件若拋出ENOENT目錄不存在則先用mkdir(dirname, { recursive: true })建目錄再寫(xiě)入其他異常則原樣向上拋出。這也解釋了為何示例中__snapshots__/schema/schema.graphql這樣的深層路徑可以一次成功。buildSchemaSync 同步版本如果項(xiàng)目環(huán)境不適合異步構(gòu)建例如某些啟動(dòng)腳本或同步初始化流程可以使用buildSchemaSync。它與buildSchema接受完全相同的BuildSchemaOptions包括emitSchemaFile的三種形態(tài)內(nèi)部調(diào)用emitSchemaDefinitionFileSync同步寫(xiě)盤(pán)見(jiàn) src/utils/buildSchema.ts。異步/同步兩種 API 由emitSchemaDefinitionFile基于fs/promises與emitSchemaDefinitionFileSync基于fs分別支撐。方式二程序化調(diào)用 emitSchemaDefinitionFile 手動(dòng)生成第二種方式完全繞開(kāi)buildSchema在任何持有GraphQLSchema對(duì)象的地方手動(dòng)調(diào)用導(dǎo)出函數(shù)寫(xiě)文件。TypeGraphQL 從 src/utils/index.ts 導(dǎo)出emitSchemaDefinitionFile、emitSchemaDefinitionFileSync以及PrintSchemaOptions類(lèi)型、defaultPrintSchemaOptions常量。import { emitSchemaDefinitionFile } from type-graphql; // ... hypotheticalFileWatcher.watch(./src/**/*.{resolver,type,input,arg}.ts, async () { const schema getSchemaNotFromBuildSchemaFunction(); await emitSchemaDefinitionFile(/path/to/folder/schema.graphql, schema); });函數(shù)簽名見(jiàn) src/utils/emitSchemaDefinitionFile.tsexport function emitSchemaDefinitionFileSync( schemaFilePath: string, schema: GraphQLSchema, options: PrintSchemaOptions defaultPrintSchemaOptions, ): void; export async function emitSchemaDefinitionFile( schemaFilePath: string, schema: GraphQLSchema, options: PrintSchemaOptions defaultPrintSchemaOptions, ): Promisevoid;第一個(gè)參數(shù)為完整目標(biāo)文件路徑含文件名第二個(gè)參數(shù)為任意GraphQLSchema對(duì)象不要求它一定來(lái)自buildSchema上例中的getSchemaNotFromBuildSchemaFunction即示意任意來(lái)源第三個(gè)可選參數(shù)為PrintSchemaOptions省略時(shí)使用defaultPrintSchemaOptions即sortedSchema: true。典型應(yīng)用場(chǎng)景官方文檔點(diǎn)名的兩類(lèi)典型用法快照測(cè)試把該函數(shù)放進(jìn)測(cè)試腳本生成 schema 快照并與預(yù)期文件比對(duì)從而在 schema 發(fā)生意外變化時(shí)讓測(cè)試失敗本地開(kāi)發(fā)熱生成結(jié)合文件監(jiān)聽(tīng)器如上例的hypotheticalFileWatcher在.ts源文件變更時(shí)自動(dòng)重新生成 SDL保持本地隨時(shí)有一份最新 schema 可讀。進(jìn)階讓自定義指令出現(xiàn)在生成的 SDL 中TypeGraphQL 本身并不直接支持在輸出的 schema 中攜帶自定義指令custom directives原因是graphql-js的printSchema函數(shù)存在限制無(wú)法打印指令定義。如果你需要自定義指令出現(xiàn)在生成文件中就需要自行實(shí)現(xiàn)一個(gè)輸出函數(shù)借助第三方printSchema實(shí)現(xiàn)例如graphql-tools/utils提供的printSchemaWithDirectives。這一主題完整收錄于當(dāng)前版本文檔 docs/emit-schema.md實(shí)現(xiàn)示例import { GraphQLSchema, lexicographicSortSchema } from graphql; import { printSchemaWithDirectives } from graphql-tools/utils; import fs from node:fs/promises; export async function emitSchemaDefinitionWithDirectivesFile( schemaFilePath: string, schema: GraphQLSchema, ): Promisevoid { const schemaFileContent printSchemaWithDirectives(lexicographicSortSchema(schema)); await fs.writeFile(schemaFilePath, schemaFileContent); }用法與標(biāo)準(zhǔn)emitSchemaDefinitionFile完全一致const schema await buildSchema(/*...*/); await emitSchemaDefinitionWithDirectivesFile(/path/to/folder/schema.graphql, schema);自定義函數(shù)可以同時(shí)復(fù)用 TypeGraphQL 的lexicographicSortSchema排序思路保持輸出穩(wěn)定。若無(wú)需自定義指令則優(yōu)先使用內(nèi)建的emitSchemaDefinitionFile即可。測(cè)試與真實(shí)項(xiàng)目中的用法參考倉(cāng)庫(kù)中的功能測(cè)試 tests/functional/emit-schema-sdl.ts 完整覆蓋了上述全部行為可作為實(shí)現(xiàn)細(xì)節(jié)的權(quán)威佐證默認(rèn)路徑mockprocess.cwd()后emitSchemaFile: true會(huì)在工作目錄生成schema.graphql測(cè)試第 168-177 行路徑字符串emitSchemaFile: targetPath直接寫(xiě)入指定路徑測(cè)試第 158-166 行配置對(duì)象emitSchemaFile: { path, sortedSchema: false }同時(shí)生效測(cè)試第 179-192 行傳空對(duì)象{}時(shí)回落默認(rèn)路徑與默認(rèn)排序測(cè)試第 194-205 行排序行為checkSchemaSDL斷言sortedSchema: true時(shí)descriptionProperty排在normalProperty之前字母序false時(shí)保持定義順序測(cè)試第 57-69 行錯(cuò)誤傳播寫(xiě)入或建目錄遇到非ENOENT異常時(shí)錯(cuò)誤會(huì)原樣拋出測(cè)試第 89-113、134-154 行同步版本buildSchemaSync與emitSchemaDefinitionFileSync的行為逐項(xiàng)等價(jià)測(cè)試第 208-257 行。在真實(shí)項(xiàng)目中emitSchemaFile常與運(yùn)行環(huán)境聯(lián)動(dòng)。例如 docs/azure-functions.md 展示了按環(huán)境變量條件開(kāi)啟的做法emitSchemaFile: process.env.NODE_ENV local ? path.resolve(./src/schema.graphql) : false,這樣在本地開(kāi)發(fā)時(shí)自動(dòng)產(chǎn)出 schema 文件而在云端運(yùn)行時(shí)關(guān)閉以免寫(xiě)只讀文件系統(tǒng)。另一個(gè)例子是 docs/nestjs.md在 NestJS 集成中同樣通過(guò)emitSchemaFile: true便捷生成 SDL。這兩處都是自動(dòng)生成方式在實(shí)際工程中的典型落地形態(tài)。小結(jié)自動(dòng)生成buildSchema({ emitSchemaFile: true | 路徑 | { path?, sortedSchema? } })構(gòu)建 schema 的同時(shí)寫(xiě)盤(pán)默認(rèn)輸出到process.cwd()/schema.graphql默認(rèn)按字典序排序并自動(dòng)附帶由 TypeGraphQL 生成的警告頭、自動(dòng)創(chuàng)建缺失目錄同步場(chǎng)景可用buildSchemaSync。程序化生成emitSchemaDefinitionFile(path, schema, options?)與同步版emitSchemaDefinitionFileSync適合快照測(cè)試、文件監(jiān)聽(tīng)熱更新等需要掌控時(shí)機(jī)的場(chǎng)景schema 對(duì)象可來(lái)自任意來(lái)源。自定義指令內(nèi)建輸出基于printSchema無(wú)法打印指令定義需要自定義輸出函數(shù)如借助printSchemaWithDirectives后以相同方式調(diào)用。兩種方式均以 src/utils/emitSchemaDefinitionFile.ts 為統(tǒng)一實(shí)現(xiàn)核心文件寫(xiě)入細(xì)節(jié)封裝在 src/helpers/filesystem.ts完整行為由 tests/functional/emit-schema-sdl.ts 驗(yàn)證可按需深入源碼進(jìn)一步探索。贊分享后端GraphQLAPI設(shè)計(jì)【免費(fèi)下載鏈接】type-graphqlCreate GraphQL schema and resolvers with TypeScript, using classes and decorators!項(xiàng)目地址https://gitcode.com/gh_mirrors/ty/type-graphql點(diǎn)擊查看免費(fèi)下載相關(guān)推薦TypeGraphQL 輸出 Schema SDL從 buildSchema 自動(dòng)生成到程序化導(dǎo)出與自定義指令TypeGraphQL 輸出 Schema SDL從 buildSchema 自動(dòng)生成到程序化導(dǎo)出與自定義指令 TypeGraphQL 的核心特性是僅憑 Ty后端GraphQLAPI設(shè)計(jì)TypeGraphQL Schema SDL 生成指南用 buildSchema 與 emitSchemaDefinitionFile 將 GraphQL Schema 導(dǎo)出為文件TypeGraphQL Schema SDL 生成指南用 buildSchema 與 emitSchemaDefinitionFile 將 GraphQL S后端GraphQLAPI設(shè)計(jì)TypeGraphQL 輸出 Schema SDL 文件全指南從 emitSchemaFile 到程序化導(dǎo)出與自定義指令TypeGraphQL 輸出 Schema SDL 文件全指南從 emitSchemaFile 到程序化導(dǎo)出與自定義指令 導(dǎo)讀 TypeGraphQL 的核心后端GraphQLAPI設(shè)計(jì)上一篇三分鐘裝好胡桃工具箱 Snap.Hutao原神抽卡保底不再手記下一篇零基礎(chǔ)10分鐘做出MapleStory MODHarepacker復(fù)活版資源編輯與地圖創(chuàng)作完整指南創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考