- 后端
- 数据库
- GraphQL
【免费下载链接】prisma1
💾 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL & MongoDB) [deprecated]
本文以 Prisma 项目(当前仓库gh_mirrors/pr/prisma1)的官方教程为蓝本,演示如何在已有的 Prisma 服务之上,用graphql-yoga与prisma-binding搭建一个面向业务领域的 GraphQL 服务器。你将掌握应用 Schema(Application Schema)与 Prisma 数据库 Schema 的分层设计、通过.graphqlconfig.yml管理双 API、以及用一行行 resolver 把查询/变更委托给 Prisma GraphQL API 的完整流程。读完后,你可以独立从零复刻出一个可运行、可在 Playground 中验证的博客类 GraphQL 后端。
为什么不在客户端直接使用 Prisma API?
Prisma 把数据库变成了一个 GraphQL API,自动暴露对数据的强大 CRUD 操作。既然如此,为什么不干脆让客户端直接调用 Prisma 的 API、省掉再写一层 GraphQL 服务器的工作?
原因在于:让客户端直连 Prisma,就等同于把你的整个数据库直接暴露给客户端。这在生产环境中并不可取,主要有以下几点考量:
- 客户端应当消费**领域特定(domain-specific)**的 API,而不是与通用 CRUD 操作打交道;
- 你需要为使用者提供认证/授权功能(如密码注册、第三方认证提供商);
- 你的 API 需要与微服务或其他遗留系统集成;
- 你需要把Stripe、GitHub、Yelp 等第三方服务/公共 API整合进服务器逻辑;
- 你不希望把整个数据库 Schema 暴露给所有人(GraphQL 的 introspection 特性会把 Schema 全部公开)。
因此,标准的架构是:客户端 → 你的 GraphQL 服务器(领域 API)→ Prisma 服务(数据库 API)。本教程正是教你搭好中间这一层。
前置准备:一个可运行的 Prisma 服务
本教程假设你已经有一个正在运行的 Prisma 服务,即至少拥有以下两个文件:
prisma.yml:服务配置文件;datamodel.graphql:数据模型定义文件。
同时请确保你能拿到该服务的endpoint(它定义在prisma.yml的endpoint属性中)。如果还没有 Prisma 服务,请先完成下列任一入门教程再回来:
- 在 Demo 服务器上搭建 Prisma
- 用全新的 MySQL 数据库搭建 Prisma
- 用全新的 Postgres 数据库搭建 Prisma
- 连接你的空 MySQL 数据库搭建 Prisma
- 连接你的空 Postgres 数据库搭建 Prisma
关于
prisma.yml的完整字段说明(datamodel、endpoint、secret、hooks、subscriptions、seed、变量引用等),可参考 prisma.yml 配置参考。
Step 1:更新数据模型
为了让后续步骤有一个合适的数据模型,需要先调整已有 Prisma 服务的数据模型。这里假设你的数据模型存放在单个文件datamodel.graphql中(如果不是,请相应调整)。
打开datamodel.graphql,把内容更新为:
type User { id: ID! @unique name: String! posts: [Post!]! } type Post { id: ID! @unique title: String! content: String! published: Boolean! @default(value: "false") author: User! }保存文件后,打开终端并进入 Prisma 服务的根目录(即prisma.yml所在的目录),运行以下命令更新其 GraphQL API:
prisma deploy部署完成后,Prisma 服务的 GraphQL API 会为数据模型中定义的User与Post类型暴露对应的 CRUD 操作,同时允许你通过connect等方式修改二者之间的关系(relation)——后面的 resolver 实现正是依赖了这一点。
Step 2:用graphql-yoga搭建 GraphQL 服务器
接下来创建 GraphQL 服务器的目录结构并安装 NPM 依赖。注意:当前存放 Prisma 服务文件(prisma.yml与datamodel.graphql)的目录,稍后会被移入 GraphQL 服务器目录内部。
在新目录中执行以下命令:
mkdir -p my-yoga-server/src touch my-yoga-server/src/index.js touch my-yoga-server/src/schema.graphql cd my-yoga-server yarn init -y这会创建预期的目录结构,并生成package.json。其中:
index.js是服务器的入口文件;schema.graphql定义应用 Schema(即你的服务器对外暴露的 GraphQL API 的 Schema)。
接着,把 Prisma 服务的根目录移动进my-yoga-server,并重命名为prisma。
完成后,my-yoga-server的结构应类似:
my-yoga-server │ ├── package.json ├── prisma │ ├── datamodel.graphql │ └── prisma.yml └── src ├── schema.graphql └── index.js接下来安装两个核心依赖:
yarn add graphql-yoga prisma-binding两个依赖的分工如下:
graphql-yoga:提供 GraphQL 服务器的功能(基于 Express.js);prisma-binding:让你可以轻松把 resolver 连接到 Prisma 的 GraphQL API——这正是本教程的技术核心。
Step 3:定义应用 Schema
每个 GraphQL 服务器的 API 都由对应的 GraphQL Schema 定义。现在来定义服务器对外暴露的操作。从数据模型可以看出,我们正在构建一个简单的博客应用:客户端不应该能对Post、User类型为所欲为,而是消费面向业务领域、按需裁剪的操作。
打开schema.graphql,添加如下 Schema 定义:
# import Post from './generated/prisma.graphql' # import User from './generated/prisma.graphql' type Query { posts(searchString: String): [Post!]! user(id: ID!): User } type Mutation { createDraft(authorId: ID!, title: String!, content: String!): Post publish(id: ID!): Post deletePost(id: ID!): Post signup(name: String!): User! }这个 Schema 定义了 6 个操作(2 个查询 + 4 个变更):
| 操作 | 签名 | 说明 |
|---|---|---|
posts | posts(searchString: String): [Post!]! | 获取全部Post,可传入searchString进行过滤 |
user | user(id: ID!): User | 按id获取单个User |
createDraft | createDraft(authorId: ID!, title: String!, content: String!): Post | 为指定authorId的User创建草稿(即published为false的Post) |
publish | publish(id: ID!): Post | 发布草稿(把published置为true) |
deletePost | deletePost(id: ID!): Post | 删除一篇Post |
signup | signup(name: String!): User! | 通过name创建新User |
这里有一点需要注意:Post和User类型是从src/generated/prisma.graphql导入的——这个文件目前还不存在,不用担心,下一步就会把它下载下来。另外,导入语法使用的是GraphQL 注释,这些注释绝不能删除!它们被graphql-import使用——该工具允许你在不同文件之间导入 SDL 类型(这是标准 SDL 目前尚不支持的能力)。
仓库佐证:
graphql-import的能力在 Prisma 生态中承担"跨文件导入 SDL 类型"的职责,正是它让schema.graphql能以# import Post from './generated/prisma.graphql'的方式复用 Prisma 数据库 Schema 中的类型。
Step 4:下载 Prisma 数据库 Schema
下一步是把 Prisma GraphQL API 的 Schema(即Prisma database schema)下载到项目中,以便从那里导入 SDL 类型。
严格来说,这一步并非绝对必要——你也可以在schema.graphql中重新定义一份一模一样的Post、User类型。但那样的话,类型定义会存在于两个相互独立的位置,今后每次更新类型都要改两遍。因此最佳实践是:从 Prisma 的 GraphQL Schema 中导入类型,保证单一事实来源。
下载 Prisma database schema 需要用到 GraphQL CLI 和 GraphQL Config。首先全局安装 GraphQL CLI:
yarn global add graphql-cli接着在服务器根目录(即my-yoga-server目录)创建.graphqlconfig:
touch .graphqlconfig.yml在其中写入以下内容,定义本项目涉及的两个 GraphQL API(Prisma 的 GraphQL API 以及你graphql-yoga服务器的定制 API):
projects: app: schemaPath: src/schema.graphql extensions: endpoints: default: http://localhost:4000 prisma: schemaPath: src/generated/prisma.graphql extensions: prisma: prisma/prisma.yml这个文件中的信息会被 GraphQL CLI 以及 GraphQL Playground 使用——在 Playground 中,你可以基于它并排操作两个 API。
最后,运行下面的命令把 Prisma database schema 下载到src/generated/prisma.graphql:
graphql get-schema --project prisma现在,包含完整数据库 CRUD API 的 Prisma database schema 已经出现在.graphqlconfig.yml中projects.prisma.schemaPath指定的位置(即src/generated/prisma.graphql),schema.graphql里的导入语句可以正常工作了。
💡Pro tip:如果你希望在每次向 Prisma 服务部署变更(例如更新数据模型)后自动更新Prisma database schema,可以在
prisma.yml中添加如下post-deploy hook:hooks: post-deploy: - graphql get-schema -p prisma
hooks.post-deploy是prisma.yml的官方配置项(详见 prisma.yml 配置参考),其中演示的正是"部署后下载 GraphQL schema 并触发代码生成"的典型用法。
Step 5:实例化 Prisma binding
实现 resolver 之前的最后一步,是确保这些 resolver 能通过Prisma binding访问 Prisma 的 GraphQL API。做法是:实例化一个 Prisma binding,并把它挂到贯穿整个 resolver 链的context对象上。
打开index.js,添加如下代码——注意把__YOUR_PRISMA_ENDPOINT__占位符替换为你的 Prisma API 的 endpoint(可在prisma.yml中找到):
const { GraphQLServer } = require('graphql-yoga') const { Prisma } = require('prisma-binding') const resolvers = { Query: { posts: (_, args, context, info) => { // ... }, user: (_, args, context, info) => { // ... } }, Mutation: { createDraft: (_, args, context, info) => { // ... }, publish: (_, args, context, info) => { // ... }, deletePost: (_, args, context, info) => { // ... }, signup: (_, args, context, info) => { // ... } } } const server = new GraphQLServer({ typeDefs: 'src/schema.graphql', resolvers, context: req => ({ ...req, prisma: new Prisma({ typeDefs: 'src/generated/prisma.graphql', endpoint: '__YOUR_PRISMA_ENDPOINT__', }), }), }) server.start(() => console.log(`GraphQL server is running on http://localhost:4000`))稍后我们会为这些resolvers补上真正实现。同样记得替换__YOUR_PRISMA_ENDPOINT__占位符——这个 endpoint 定义在prisma.yml的endpoint属性中。
关于Prisma构造器的可配置项,可参考 Prisma Bindings API 参考:除了教程中用到的typeDefs(Prisma 服务 Schema 定义文件的路径)与endpoint(Prisma 服务端点)之外,还支持secret(服务密钥,用于签发 JWT)、fragmentReplacements(resolver 正常运行所必需的 GraphQL fragment 列表,可选)以及debug(为true时把所有查询/变更打印到控制台,默认false)。
Step 6:用 Prisma bindings 实现 resolver
把Prismabinding 实例挂到context之后,所有 resolver 函数都能访问它并调用其 binding 函数。于是,resolver 不再需要直接访问数据库,而是把传入查询的执行委托(delegating)给 Prisma 的 GraphQL API。
binding 函数提供了一套便捷的 JavaScript API 来向 GraphQL API 发送查询与变更。妙处在于:你可以把info对象原样传递——它包含了客户端在查询中请求了哪些字段的信息,这样 Prisma 就能非常高效地解析查询。
在index.js中为 resolver 函数补上实现:
const resolvers = { Query: { posts: (_, args, context, info) => { return context.prisma.query.posts( { where: { OR: [ { title_contains: args.searchString }, { content_contains: args.searchString }, ], }, }, info, ) }, user: (_, args, context, info) => { return context.prisma.query.user( { where: { id: args.id, }, }, info, ) }, }, Mutation: { createDraft: (_, args, context, info) => { return context.prisma.mutation.createPost( { data: { title: args.title, content: args.content, author: { connect: { id: args.authorId, }, }, }, }, info, ) }, publish: (_, args, context, info) => { return context.prisma.mutation.updatePost( { where: { id: args.id, }, data: { published: true, }, }, info, ) }, deletePost: (_, args, context, info) => { return context.prisma.mutation.deletePost( { where: { id: args.id, }, }, info, ) }, signup: (_, args, context, info) => { return context.prisma.mutation.createUser( { data: { name: args.name, }, }, info, ) }, }, }每个 resolver 的实现都只是一次 binding 函数的调用:把操作委托给 Prisma API,省去了任何手动数据库访问。可以看到几个关键模式:
- 查询委托:
context.prisma.query.posts({ where: {...} }, info)/context.prisma.query.user({ where: { id } }, info),通过where传递过滤条件; - 创建 + 关系连接:
createPost的data.author.connect把新Post通过关系连接到指定User; - 更新:
updatePost用where定位、用data指定新值(发布草稿即把published置为true); - 删除:
deletePost仅需where定位。
从仓库源码看,这类 binding 客户端在 cli/packages/prisma-client-lib 中实现:Client.ts 的构造函数会用typeDefs构建 GraphQL Schema、基于secret签发 JWT,并通过BatchedGraphQLClient(带Authorization: Bearer <token>请求头)向 endpoint 发起请求;query/mutation/exists等属性则由 makePrismaClientClass.ts 工厂方法动态生成,其内部把一次 binding 调用翻译成一次真实的 GraphQL HTTP 请求——这正是"resolver 委托给 Prisma"背后的底层机制。
补充:除
query/mutation之外,binding 还提供exists.<Type>(where)用于判断某类型节点是否满足条件(返回布尔值),以及更底层的request(query, variables)方法(需要你手写完整 GraphQL 查询串),详见 Prisma Bindings API 参考。
Step 7:启动服务器并在 Playground 中测试
至此,GraphQL 服务器的实现已经完成,可以在 GraphQL Playground 中使用了。
在终端中启动服务器:
node src/index.js然后在浏览器中访问http://localhost:4000打开 GraphQL Playground。
💡Pro tip:除了直接访问 URL,你也可以在终端运行
graphql playground命令——它会读取.graphqlconfig.yml中的信息,让你并排使用两个 GraphQL API(应用 API 与 Prisma API)。
接下来,在 Playground 中依次发送下面的查询与变更来验证 API:
创建新用户:
mutation { signup(name: "Alice") { id } }为某用户创建草稿(把__USER_ID__替换为数据库中真实User的id):
mutation { createDraft( title: "Join us at GraphQL Europe 🇪🇺 ", content: "Get a 10%-discount with this promo code on graphql-europe.org: gql-boilerplates", authorId: "__USER_ID__" ) { id published } }发布草稿(把__POST_ID__替换为数据库中真实Post的id):
mutation { publish( id: "__POST_ID__", ) { id published } }按关键词过滤文章:
query { posts(searchString: "GraphQL Europe") { id title content published author { id name } } }删除文章(把__POST_ID__替换为数据库中真实Post的id):
mutation { deletePost( id: "__POST_ID__", ) { id } }小结与延伸阅读
回顾整个流程:我们以一个已有 Prisma 服务为基础,通过 7 个步骤搭出了一层领域特定的 GraphQL 服务器——更新数据模型 → 搭建graphql-yoga项目 → 定义应用 Schema → 用 GraphQL CLI 下载 Prisma database schema → 实例化 Prisma binding 挂到context→ 用一行行"委托式" resolver 连接两层 API → 在 Playground 中验证增删改查。这套"Prisma 只管数据库 CRUD、上层服务器专注业务领域"的分层架构,正是 Prisma 在 GraphQL 后端开发中的典型落地方式。
想进一步深入,可以在本仓库中继续阅读:
- Prisma Bindings 概览:binding 的设计动机、工作流程与示例;
- Prisma Bindings API:构造器参数、
query/mutation/exists/request的完整用法; - Prisma Bindings 代码生成:动态 binding 与静态(代码生成)binding 的取舍;
- prisma.yml 配置参考:
endpoint、secret、hooks、subscriptions、seed等配置项的完整说明; - Prisma 客户端源码:binding/client 底层如何用
BatchedGraphQLClient发送请求、如何基于secret签发 JWT。
- 后端
- 数据库
- GraphQL
【免费下载链接】prisma1
💾 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL & MongoDB) [deprecated]
相关推荐
open-code-review 内置 Bicep 审查规则详解:面向 Azure IaC 的静态安全与质量审查清单
open code review 内置 Bicep 审查规则详解:面向 Azure IaC 的静态安全与质量审查清单 本文围绕 open code review
后端数据库GraphQL基于 Prisma 服务构建 GraphQL 服务器:使用 graphql-yoga 与 prisma-binding 的完整实战
基于 Prisma 服务构建 GraphQL 服务器:使用 graphql yoga 与 prisma binding 的完整实战 本文是一篇完整的实战指南:以
后端数据库GraphQLPrisma Bindings 实战指南:用 prisma-binding 构建基于 Prisma 服务的 GraphQL 服务器
Prisma Bindings 实战指南:用 prisma binding 构建基于 Prisma 服务的 GraphQL 服务器 本篇指南围绕 prisma
后端数据库GraphQL
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考