graphql-yoga Hackernews 示例实战:用 GraphQL Yoga + Prisma 从零构建完整 GraphQL API
后端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点击查看免费下载examples/hackernews/README.md 是 GraphQL Yoga 仓库中官方教程对应的完整可运行示例演示了如何用graphql-yoga与 PrismaSQLite搭建一个 HackerNews 克隆的 GraphQL 服务涵盖 Schema 设计、Context 注入、分页与过滤约束、错误处理、类型化代码生成和集成测试验证。本文以该示例文档为骨架深入其源码与配置讲清楚这个教程应用是如何被组装、运行和测试的。示例定位与技术栈该示例的 README 明确声明了两点事实技术栈GraphQL Yoga Prisma来源它是官方 GraphQL Yoga 教程Hackernews 克隆教程中逐步构建出的最终应用教程的仓库内文档位于 website/content/tutorial/basic/从项目搭建到过滤器与分页共 12 步01-project-setup.mdx至12-summary.mdx。包名example-hackernews见 package.json依赖极简核心运行时依赖只有两个graphql17.0.2与graphql-yogaworkspace 内部包开发依赖包含prisma/prisma/client6.19.0、typescript、ts-node以及 GraphQL Code Generator 相关工具。运行方式README 给出的完整操作步骤# 执行数据库迁移 pnpm run --filter example-hackernews migrate # 启动开发服务器 pnpm run --filter example-hackernews dev然后访问http://localhost:4000/即可看到 GraphiQL 界面。对照 package.json 的 scripts这些命令实际执行的是脚本实际命令作用migrateprisma migrate dev基于 prisma/schema.prisma 创建/更新数据库结构devcross-env NODE_ENVdevelopment ts-node-dev --exit-child --respawn src/main.ts以开发模式热重载启动服务startts-node src/main.ts非热重载方式启动postinstallpnpm run prisma:generate pnpm run codegen安装后自动生成 Prisma Client 与 GraphQL 类型codegengraphql-codegen依据 codegen.ts 生成类型postinstall钩子保证了克隆仓库并安装依赖后Prisma Client 与 GraphQL 生成类型自动就绪开发者无需手动执行代码生成步骤。服务入口Yoga 实例如何挂载到 HTTP 服务器入口文件 src/main.ts 只有 14 行展示了 Yoga 最核心的集成模式——Yoga 实例本身就是一个 Fetch handler可以直接作为 Node.js HTTP 服务器的请求处理器import { createServer } from node:http; import { createYoga } from graphql-yoga; import { createContext } from ./context; import { schema } from ./schema; function main() { const yoga createYoga({ schema, context: createContext }); const server createServer(yoga); server.listen(4000, () { console.info(Server is running on http://localhost:4000${yoga.graphqlEndpoint}); }); } main();三个关键点createYoga({ schema, context: createContext })传入 Schema 和一个异步 context 工厂函数Yoga 会在每个请求执行时调用它来构造执行上下文createServer(yoga)Node 的http.createServer接受 Yoga 实例作为 handler这正是 Yoga “核心实现 WHATWG Fetch API”特性的体现——同一个 handler 可部署到任何 JS 环境yoga.graphqlEndpointGraphQL 端点路径默认/graphql日志中拼接它让开发者明确 POST 查询的目标地址这也是 README 中“访问http://localhost:4000/”后 GraphiQL 页面能连上http://localhost:4000/graphql的原因。Context把 Prisma 客户端注入 GraphQL 执行上下文src/context.ts 完成了教程第 7 步“连接服务器与数据库”的核心工作import { PrismaClient } from prisma/client; const prisma new PrismaClient(); export type GraphQLContext { prisma: PrismaClient; }; export async function createContext(): PromiseGraphQLContext { return { prisma }; }实现细节值得注意PrismaClient是模块级单例而非每请求新建——连接池只在应用生命周期内初始化一次而 context 工厂只是把该单例暴露给所有 resolver。GraphQLContext类型随后会被代码生成系统引用见下文 codegen 配置中的contextType: ../context#GraphQLContext使每个 resolver 的第三个参数context自动获得类型推导。数据层Prisma SQLite 的数据模型prisma/schema.prisma 定义了数据源与两个模型datasource db { provider sqlite url file:./dev.db } generator client { provider prisma-client-js } model Link { id Int id default(autoincrement()) createdAt DateTime default(now()) description String url String comments Comment[] } model Comment { id Int id default(autoincrement()) createdAt DateTime default(now()) body String link Link relation(fields: [linkId], references: [id]) linkId Int }使用 SQLite 文件数据库dev.db零配置即可运行Link与Comment之间是一对多关系外键Comment.linkId指向Link.id。对应的物理表结构由两次迁移生成prisma/migrations/20220223111842_init/migration.sql 创建Link表id、createdAt、description、urlprisma/migrations/20220223114846_comments/migration.sql 创建Comment表并建立外键约束Comment_linkId_fkeyON DELETE RESTRICT ON UPDATE CASCADE。执行 README 中的migrate脚本后这些 SQL 就会落地到本地 SQLite 文件中。GraphQL Schema查询、变更与嵌套类型API 契约定义在 src/schema/base/schema.graphql是典型的代码生成式项目布局文件按schema/目录组织供 codegen 工具按 glob 收集type Query { info: String! feed(filterNeedle: String, skip: Int, take: Int): [Link!]! comment(id: ID!): Comment link(id: ID!): Link } type Mutation { postLink(url: String!, description: String!): Link! postCommentOnLink(linkId: ID!, body: String!): Comment! } type Link { id: ID! description: String! url: String! comments: [Comment!]! } type Comment { id: ID! createdAt: String! body: String! link: Link! }设计上覆盖了一个内容站的最小闭环Query.info静态信息返回教程中用于验证服务器跑通Query.feed带filterNeedle全文模糊匹配与skip/take分页参数的链接信息流Query.comment/Query.link按 ID 单查Mutation.postLink发布新链接Mutation.postCommentOnLink给指定链接发评论Link.comments/Comment.link双向嵌套访问GraphQL 客户端可在一次查询中展开关联数据。Resolver 实现细节feed过滤 受约束的分页src/schema/base/resolvers/Query/feed.ts 是教程第 10 步“过滤与分页”的落地实现export const feed: NonNullableQueryResolvers[feed] async (_parent, args, context) { const where args.filterNeedle ? { OR: [ { description: { contains: args.filterNeedle } }, { url: { contains: args.filterNeedle } }, ], } : {}; const take applyTakeConstraints({ min: 1, max: 50, value: args.take ?? 30 }); const skip applySkipConstraints(args.skip ?? 0); return context.prisma.link.findMany({ where, skip, take }); };行为要点filterNeedle通过 Prisma 的ORcontains实现“描述或 URL 命中即可”的模糊过滤take默认 30、合法区间[1, 50]skip默认 0 且必须非负越界参数不会静默截断而是直接抛出 GraphQLError 拒绝请求——约束逻辑集中在 src/utils.tsexport const applySkipConstraints (value: number) { if (value 0) { throw new GraphQLError(skip argument value ${value} is invalid, value must be positive.); } return value; }; export const applyTakeConstraints (params: { min: number; max: number; value: number }) { if (params.value params.min || params.value params.max) { throw new GraphQLError( take argument value ${params.value} is outside the valid range of ${params.min} to ${params.max}., ); } return params.value; };这是一种值得借鉴的 API 防御式写法把“分页参数合法性”作为契约错误error而非静默降级返回客户端能明确感知参数非法。postCommentOnLink多层错误处理src/schema/base/resolvers/Mutation/postCommentOnLink.ts 集中体现了教程第 9 步“错误处理”的三个层次参数格式校验parseIntSafe(args.linkId)同样位于 utils.ts用正则/^(\d)$/保证 linkId 是纯数字非法时返回nullresolver 随即 reject 一个GraphQLErrorCannot post comment on non-existing link with id ...业务校验评论正文为空或全空白时 rejectComment body cannot be empty.数据库级联错误转换创建评论时捕获 Prisma 错误若错误是Prisma.PrismaClientKnownRequestError且code P2003外键约束失败即 linkId 不存在则转换为与第 1 层一致的友好错误信息其余错误原样抛出。postLinkresolvers/Mutation/postLink.ts则是最直接的写入调用context.prisma.link.create并返回新建的Link。关联类型与标量映射对象级 resolver 展示了两个典型细节resolvers/Link.tsLink.comments按createdAt倒序取当前链接的全部评论resolvers/Comment.tsComment.link用findUniqueOrThrow反查所属链接createdAt字段特意覆写为createdAt.toISOString()——源码注释说明原因是 Prisma 返回的DateTimeDate对象与生成的CommentMapper.createdAtstring类型不兼容需要在此显式序列化为 ISO 字符串以匹配 Schema 中createdAt: String!的声明。类型安全组装createSchema GraphQL Code Generatorsrc/schema.ts 展示了 Yoga 推荐的 Schema 组装方式import { createSchema } from graphql-yoga; import { resolvers } from ./schema/resolvers.generated; import { typeDefs } from ./schema/typeDefs.generated; export const schema createSchema({ resolvers: [resolvers], typeDefs: [typeDefs], });resolvers.generated与typeDefs.generated两个文件由代码生成产出生成规则定义在 codegen.tsconst config: CodegenConfig { schema: src/schema/**/schema.graphql, generates: { src/schema: defineConfig({ resolverGeneration: disabled, typesPluginsConfig: { contextType: ../context#GraphQLContext, }, }), }, hooks: { afterAllFileWrite: [prettier --write] }, };关键配置解读schema: src/schema/**/schema.graphql按 glob 收集目录下所有 Schema 文件这是“按 resolver 文件组织代码”布局的前提eddeee888/gcg-typescript-resolver-files插件教程第 11 步引入会根据目录约定Query/、Mutation/、Link.ts等文件名自动聚合各 resolver 文件导出生成resolvers.generated.tscontextType: ../context#GraphQLContext生成类型时引用 context 导出的GraphQLContext于是feed、postCommentOnLink等 resolver 签名中的context.prisma都享有完整类型检查写文件后自动执行prettier --write格式化。package.json中postinstall/precheck钩子会在安装和检查前自动执行codegen保证生成的类型始终与 Schema 同步。集成测试不启动端口也能验证整个 APIintegration-tests/hackernews.spec.ts 展示了对该示例的自动化验证方式其思路是绕过真实端口直接调用 Yoga 的 fetch handlerbeforeAll中先用prisma/migrate的MigrateDev对prisma/schema.prisma执行迁移再通过PrismaClient插入一条种子数据https://www.prisma.io/ “Prisma replaces traditional ORMs”测试通过yoga.fetch(http://yoga/graphql, { method: POST, body: JSON.stringify({ query: ... }) })发送 GraphQL 操作断言响应快照例如创建评论的 mutationmutation CreateComment { postCommentOnLink(body: Comment on post, linkId: 1) { body link { description id url } } }期望响应为{ data: { postCommentOnLink: { body: Comment on post, link: { description: Prisma replaces traditional ORMs, id: 1, url: https://www.prisma.io } } } }这同时印证了 README 中dev命令启动后浏览器访问 GraphiQL 所调用的就是同一执行链路schemacreateContext3.afterAll用DbDrop标记为 preview feature需--preview-feature --force清库保证测试可重复执行。小结与延伸这个 25 行的 README 背后是一个结构完整、可直接运行的参考实现其目录组织对自建项目有直接的参考价值关注点文件学到的模式服务器挂载src/main.tsYoga 作为 Fetch handler 直接挂到 Node HTTP 服务器端口 4000Context 注入src/context.ts模块级单例 Prisma 客户端经异步工厂暴露给 resolver数据模型prisma/schema.prismaSQLite Prisma 零配置持久化迁移 SQL 版本化存放API 契约src/schema/base/schema.graphqlQuery/Mutation/关联类型的最小内容站闭环防御式分页src/utils.tsskip/take 越界即抛 GraphQLError错误处理postCommentOnLink.ts参数校验、业务校验、Prisma P2003 外键错误三层转换类型生成codegen.tsresolver-files 布局 contextType全链路类型推导自动化验证hackernews.spec.tsyoga.fetch直测 handler 迁移/清库生命周期管理若希望复现教程的逐步过程可对照仓库内的教程文档 website/content/tutorial/basic/从01-project-setup.mdx到12-summary.mdx按 commit 粒度逐步展开本示例即为该教程第 12 步完成态的代码快照。赞分享后端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 与 Prisma 从零构建 GraphQL Server完整实战教程使用 graphql yoga 与 Prisma 从零构建 GraphQL Server完整实战教程 导读 本文以 Prisma 开源仓库 1.12 文档中的后端数据库GraphQL从零构建 GraphQL 服务器基于 graphql-yoga 与 Prisma 的博客 API 完整实战教程从零构建 GraphQL 服务器基于 graphql yoga 与 Prisma 的博客 API 完整实战教程 本篇技术指南以 Prisma 官方文档 doc后端数据库GraphQL使用 Prisma 与 TypeScript 引导构建 GraphQL 服务器graphql-yoga prisma-binding 完整实战使用 Prisma 与 TypeScript 引导构建 GraphQL 服务器graphql yoga prisma binding 完整实战 导读 本教后端数据库GraphQL上一篇构建革命性AI代理系统掌握DeepAgents的完整智能代理框架下一篇tcomment_vim自定义指南打造属于你的个性化注释风格创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考