使用Prisma构建连接到MySQL的GraphQL API服务器
首先
我们将基于Prisma和graphql-yoga,按照Prisma官方的基础教程“从零开始构建GraphQL服务器”,来编写GraphQL服务器。
本篇文章以与上述教程略有不同的方式表述
-
- 言語:TypeScript
- DB:ローカルに立てるDocker上のMySQL
我将为您介绍。
在开始教程之前,对于Prisma是什么的人,请问。
-
- prisma – 最速 GraphQL Server実装
- Prisma.ioでGraphQL APIサーバーを楽して作る
阅读周围的内容,大致了解Prisma,并参考官方的方式。
- Prisma Introduction: What, Why & How
建议您试着确认一下。
目标读者
-
- GraphQLに関する知識をある程度有している方
-
- GraphQL APIを提供するサーバーを簡単に素早く作りたい方
-
- データベースは自前のMySQLを使いたい方
- 言語はTypeScriptで書きたい方
执行环境
-
- Mac OS Mojave: v10.14.2
-
- node: v11.2.0
-
- npm: v6.5.0
-
- yarn: v1.12.3
-
- docker: v17.09.1-ce
-
- prisma: v1.23.4
- graphql: v3.0.4
構築GraphQL API服务器(用于前端的后端)
首先,我们将开始构建面向外部提供GraphQL API的BFF。
随后,我们将构建一个不向外部公开的针对内部的Prisma服务器进行封装的架构。
创建并初始化NPM项目目录
mkdir prisma-yoga-mysql-typescript-sample
cd prisma-yoga-mysql-typescript-sample
npm init -y
创建src/index.ts文件
mkdir src
touch src/index.ts
安装graphql-yoga
yarn add graphql-yoga
安装nodemon、ts-node和TypeScript
在这个教程中,我们将在src/index.js中编写代码并执行。然而,为了在TypeScript中完成同样的操作,我们需要安装ts-node库,它可以让我们直接在Node.js上运行TS文件而无需编译,并且还需要安装nodemon来监视文件,并在文件更改时重新启动Node进程。
yarn add -D nodemon ts-node typescript
更新package.json文件
为了能够通过运行”yarn start”脚本,将package.json文件中的scripts字段的值修改为以下内容。
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "nodemon --ext ts,yaml,graphql --exec 'ts-node' src/index.ts"
},
只监视扩展名为ts、yaml和graphql的文件。
新增写入到 src/index.ts 文件中
import { GraphQLServer } from 'graphql-yoga';
// GraphQL SDLに沿ってschemaを定義
const typeDefs = `
type Query {
description: String
}
`;
// GraphQL APIのリクエストに応えるための実装
const resolvers = {
Query: {
description: () => `This is the API for a simple blogging application`,
},
};
const server = new GraphQLServer({
typeDefs,
resolvers,
});
server.start(() =>
console.log(`The server is running on http://localhost:4000`),
);
当您在此保存时,如果在命令行上执行”yarn start”,则应该会在http://localhost:4000上启动服务器。
如果成功执行以下查询并返回结果,则访问http://localhost:4000。
{
description
}

创建src/schema.graphql
为了重新定义GraphQL模式,并且与已经分配给src/index.ts的typeDefs不同,我们将创建一个名为schema.graphql的文件。
touch src/schema.graphql
type Query {
posts: [Post!]!
post(id: ID!): Post
description: String!
}
type Mutation {
createDraft(title: String!, content: String): Post
deletePost(id: ID!): Post
publish(id: ID!): Post
}
type Post {
id: ID!
title: String!
content: String!
published: Boolean!
}
src/index.ts的更新
为了加载上面创建的模式,请将src/index.ts按以下方式修改。
import { GraphQLServer } from 'graphql-yoga';
// GraphQL SDLに沿ってschemaを定義
// const typeDefs = `
// type Query {
// description: String
// }
// `;
// GraphQL APIのリクエストに応えるための実装
const resolvers = {
Query: {
description: () => `This is the API for a simple blogging application`,
},
};
const server = new GraphQLServer({
typeDefs: './src/schema.graphql',
resolvers,
});
server.start(() =>
console.log(`The server is running on http://localhost:4000`),
);
在每个保存之后,如果由nodemon重新启动了进程,请直接访问http://localhost:4000,如果模式已经反映如下,那就没问题了。

搭建针对Prisma服务器的内部端构建。
从这里开始使用Prisma构建一个面向内部的服务器。
即将创建的面向内部的GraphQL API服务器将直接对数据库执行查询。
Prisma CLI的全局安装
如果您尚未能够使用prisma命令,请进行全局安装。
(附注:本文章假设Prisma的版本为1.23.4,如果CLI未安装1.23.4版本,它将无法正常运行)
npm install -g prisma@1.23.4
创建数据库目录
当在项目的根目录下执行下列Prisma命令时。
prisma init database
由于我们选择使用REPL来实现,所以我们分别选择”创建新数据库”、”MySQL”和”Prisma TypeScript客户端”。请注意,这与原始教程中的第13步骤所选择的不同。
% prisma init database
? Set up a new Prisma server or deploy to an existing server?
Set up a new Prisma server for local development (based on docker-compose):
Use existing database Connect to existing database
❯ Create new database Set up a local database using Docker
Or deploy to an existing Prisma server:
Demo server Hosted demo environment incl. database (requires login)
Use other server Manually provide endpoint of a running Prisma server
? What kind of database do you want to deploy to? (Use arrow keys)
❯ MySQL MySQL compliant databases like MySQL or MariaDB
PostgreSQL PostgreSQL database
MongoDB Mongo Database
? Select the programming language for the generated Prisma client
❯ Prisma TypeScript Client
Prisma Flow Client
Prisma JavaScript Client
Prisma Go Client
Don't generate
执行命令后,应该会创建一个名为database的目录,并按照以下结构组织:
(原始教程中提到应该创建database/datamodel.graphql,但在prisima 1.23.4版本上执行时,将创建database/datamodel.prisma代替)
├── database
│ ├── datamodel.prisma // DBのテーブル構造元となる型定義
│ ├── docker-compose.yml
│ ├── generated
│ │ └── prisma-client
│ │ ├── index.ts
│ │ └── prisma-schema.ts
│ └── prisma.yml // Prismaサーバーの設定ファイル
├── package.json
├── src
├── index.ts
└── schema.graphql
数据模型.prisma的更新
请按照本教程的指导,使用以下内容重写datamodel.prisma。
type Post {
id: ID! @unique
title: String!
content: String!
published: Boolean! @default(value: "false")
}
我认为您可能能够大致想象上述内容的定义,一旦部署这个应用,它会与”Post”表进行映射,并且称为“id~published”的字段部分会被映射到各个列上。
“!”表示不为空,”@unique”表示该字段的值在整个表中是唯一的。
“@default”表示如果在插入记录时没有指定”published”的值,那么将自动分配false作为默认值。
如果您想详细了解在此出现的@unique、@default等被称为指令的内容,请参考GraphQL的官方文档等。
设置DB连接端口
以下是一个示例,用中文来重新表达给定的语句:
这个设置是可选的,但如果您想直接连接到数据库,请根据以下方式修改 database/docker-compose.yml文件中的services.mysql.ports的值。
mysql:
image: mysql:5.7
restart: always
environment:
MYSQL_ROOT_PASSWORD: prisma
volumes:
- mysql:/var/lib/mysql
ports:
- "3333:3306"
启动Docker容器
要部署Prisma,需要确保能够连接到数据库。
在命令行中,切换到包含docker-compose.yml文件的database目录,并执行docker-compose up命令,启动Docker容器。
cd database
docker-compose up
Prisma的部署
在启动了Docker容器后,使用另一个命令行窗口再次进入database目录,并执行prisma deploy命令,将其部署到database/prisma.yml文件中所指定的 http://localhost:4466 地址上。
cd database
prisma deploy
部署完成后,将同时对数据库执行迁移操作。
完成部署后,访问 http://localhost:4466 ,打开 DOCS 页面,您会发现 Post 表的增删改查功能已经实现了。

另外,如果在上述進行了用於數據庫連接的端口設定,您可以使用Sequel Pro等數據庫客戶端工具以以下設定來訪問數據庫。(用戶名、密碼和端口可以通過docker-compose.yml進行更改)

将Prisma服务器与BFF进行关联
最后,我们将对Prisma服务器进行直接操作,并将其与BFF(用于向外部公开的GraphQL API服务器)进行绑定,通过公开的GraphQL API执行Prisma服务器的GraphQL API,并使其能够对数据库进行操作。
创建与Prisma服务器的关联设置文件。
前往项目根目录,创建.graphqlconfig.yml文件,并按以下方式进行配置。
touch .graphqlconfig.yml
projects:
database:
schemaPath: src/generated/prisma.graphql
extensions:
prisma: database/prisma.yml
app:
schemaPath: src/schema.graphql
extensions:
endpoints:
default: http://localhost:4000
创建Prisma的模式文件。
执行以下命令,在上述Yaml文件的projects.database.schemaPath指定的路径中生成模式文件。
graphql get-schema
安装 prisma-binding
安装所需的库prisma-binding来进行链接。
yarn add prisma-binding
src/index.ts的更新
使用安装的prisma-binding库中的Prisma类来修改src/index.ts,以便使用src/generated/prisma.graphql文件在上面创建。
import { GraphQLServer } from 'graphql-yoga';
import { Prisma } from 'prisma-binding';
import { IResolvers } from 'graphql-middleware/dist/types';
const resolvers: IResolvers = {
Query: {
posts(parent, args, ctx, info) {
return ctx.db.query.posts({}, info);
},
post(parent, args, ctx, info) {
return ctx.db.query.post({ where: { id: args.id } }, info);
},
},
Mutation: {
createDraft(parent, { title, content }, ctx, info) {
return ctx.db.mutation.createPost(
{
data: {
title,
content,
},
},
info,
);
},
deletePost(parent, { id }, ctx, info) {
return ctx.db.mutation.deletePost({ where: { id } }, info);
},
publish(parent, { id }, ctx, info) {
return ctx.db.mutation.updatePost(
{
where: { id },
data: { published: true },
},
info,
);
},
},
};
const server = new GraphQLServer({
typeDefs: './src/schema.graphql',
resolvers,
context: req => ({
...req,
db: new Prisma({
typeDefs: 'src/generated/prisma.graphql', // the generated Prisma DB schema
endpoint: 'http://localhost:4466', // the endpoint of the Prisma DB service
// secret: "mysecret123", // specified in database/prisma.yml
debug: true, // log all GraphQL queries & mutations
}),
}),
});
server.start(() => console.log('Server is running on http://localhost:4000'));
升级 src/schema.graphql。
我们可以通过以下方式对src/schema.graphql进行修改,以便能够使用在src/generated/prisma.graphql中定义的Post类型。
# import Post from "./generated/prisma.graphql"
type Query {
posts: [Post!]!
post(id: ID!): Post
description: String!
}
type Mutation {
createDraft(title: String!, content: String): Post
deletePost(id: ID!): Post
publish(id: ID!): Post
}
最后
Prisma可以解决N+1问题吗?据下述文章所述,Prisma能解决N+1问题,因此我们希望能在实际应用中使用它。
源代码
你可以从以下的GitHub链接中找到我们最新创建的样本:
https://github.com/galoi/prisma-yoga-mysql-typescript-sample
另外,我参考了其他的文章。
- GraphQLの旅(2) Prismaで開発したGraphQL APIを公開