通过使用Apollo和Prisma来入门GraphQL
这篇文章是Hamee Advent Calendar 2019第21天的文章。
最近,我在实现某个管理界面之后意识到应该使用GraphQL进行实现。这让我重新对GraphQL产生了兴趣,因此我决定尝试一下。于是,我真的去实践了一下。
本来我计划创建一个简单的Todo应用,但最终只整理了一下GraphQL服务器的接触感觉。
t-yng/graphql-todo-app的中文释义是什么呢?
只需要一个答案:
t-yng/graphql-todo-app 是一个基于GraphQL的待办事项应用。
图书馆介绍
apollographql/apollo-server: GraphQLサーバー
実際にGraphQLのリクエストを受け付けてデータを返すサーバー
この記事ではラップされた apollo-server-express を利用していきます。
prisma/prisma: ORM
GraphQLサーバーとデータベースを繋ぐORM
prisma自体はライブラリではなくCLIツール
项目的建立
$ yarn init -y
$ yarn add typescript
$ yarn add -D ts-node
$ yarn tsc --init
安装 Apollo Server
$ yarn add apollo-server-express express @types/express
你好,世界。
实施
$ mkdir src
$ touch src/index.ts
import express from 'express';
import { ApolloServer, gql } from 'apollo-server-express';
const typeDefs = gql`
type Query {
hello: String
}
`;
const resolvers = {
Query: {
hello: () => 'Hello GrapQL!',
},
};
const server = new ApolloServer( { typeDefs, resolvers });
const app = express();
server.applyMiddleware({ app });
app.listen({ port: 4000 }, () => {
console.log(`? Server ready at http://localhost:4000${server.graphqlPath}`);
});
执行
在package.json中添加执行脚本并执行。
"scripts": {
"dev": "ts-node src/index.ts"
}
$ yarn dev
如果访问 http://localhost:4000/graphql 并执行以下查询,将返回结果。
query {
hello
}
尝试实现GraphQL接口
然后,我将尝试自己实现一个获取简单To-do的GraphQL查询。
定义模式
我会写下Todo对象的类型定义以及获取它的查询的类型定义。
※ 不确定”类型定义”这个说法是否正确。
const typeDefs = gql`
type Todo {
id: Int!
title: String
}
type Query {
todo(id: Int): Todo
todoes(title: String): [Todo]
}
`;
实现解析器
解析器是接收请求并根据架构定义返回数据的部分。假设我们定义一个变量并返回值。
const todoes = [
{
id: 1,
title: '簡単なタスク',
},
{
id: 2,
title: '気持ち簡単なタスク',
},
{
id: 3,
title: 'ヤバいタスク',
},
{
id: 4,
title: '気づかないフリをしているタスク',
}
]
const resolvers = {
Query: {
todo: (_: any, { id }: { id: number}) => {
return todoes.find(todo => todo.id === id);
},
todoes: (_: any, { title }: { title: string }) => {
return todoes.filter(todo => todo.title.includes(title))
},
},
};
进行
那么,让我们启动服务器并确认到目前为止的更改。
$ yarn dev
ts-node src/index.ts
? Server ready at http://localhost:4000/graphql
哦!结果顺利获取了(*゚▽゚ノノ゙☆パチパチ
这真是一次令人兴奋、非常有趣的新鲜体验!
与DB进行合作
只要去获取变量的数据是很无聊的,所以最好实践性地获取存在于数据库中的数据。
引入Prisma
Prisma是一种连接GraphQL服务器和数据库的ORM。只需编写模型定义,它会自动生成用于进行表迁移和访问数据库的代码。
感觉使用TypeORM等库和没有使用没有太大区别,完全没有享受到任何优势…
Prisma并非一个库,而是一个CLI工具,所以需要将其作为开发包进行安装。
$ yarn add -D prisma
数据库和Prisma的安装设置
只要在本地开发的话,执行init命令就能帮你搭建好数据库的Docker环境。
这次的设置是选择了创建新数据库、MySQL和Prisma TypeScript Client。
$ yarn prisma init .
yarn run v1.19.1
$ /Users/tomohiro/workspace/graphql/graphql-todo-app/node_modules/.bin/prisma init .
? Set up a new Prisma server or deploy to an existing server? Create new database
? What kind of database do you want to deploy to? MySQL
? Select the programming language for the generated Prisma client Prisma TypeScript Client
Created 3 new files:
prisma.yml Prisma service definition
datamodel.prisma GraphQL SDL-based datamodel (foundation for database)
docker-compose.yml Docker configuration file
Next steps:
1. Open folder: cd .
2. Start your Prisma server: docker-compose up -d
3. Deploy your Prisma service: prisma deploy
4. Read more about Prisma server:
http://bit.ly/prisma-server-overview
Generating schema... 17ms
Saving Prisma Client (TypeScript) at /Users/tomohiro/workspace/graphql/graphql-todo-app/generated/prisma-client/
启动Docker
为了启动Prisma服务器,从自动生成的 docker-compose.yml 文件中启动Docker容器。
默认情况下,数据库容器的端口未绑定到主机端口上,因此请将以下部分注释掉以访问数据库。
# Uncomment the next two lines to connect to your your database from outside the Docker environment, e.g. using a database GUI like Workbench
ports:
- "3306:3306"
$ docker-compose up -d
数据模型的定义
我們將定義數據模型。Prisma將根據這個模型定義進行數據庫表的遷移。
type Todo {
id: Int! @id(strategy: AUTO)
title: String!
}
执行迁移
在执行迁移之前,请修改 prisma.yml 文件,并进行客户端代码生成路径和 hooks 的设置。
generate:
- generator: typescript-client
output: ./src/generated/prisma-client/ # 生成先を src/ に変更
# マイグレーション実行のタイミングでクライアントコードの生成も実行する
hooks:
post-deploy:
- prisma generate
现在开始执行迁移操作。
$ yarn prisma deploy
yarn run v1.19.1
$ /Users/tomohiro/workspace/graphql/graphql-todo-app/node_modules/.bin/prisma deploy
Creating stage default for service default ✔
Deploying service `default` to stage `default` to server `local` 1.3s
Changes:
Todo (Type)
+ Created type `Todo`
+ Created field `id` of type `Int!`
+ Created field `title` of type `String!`
...
如果实际连接到MySQL,你可以确认生成了default@default/Todo数据库和选项。
host: 127.0.0.1
user: root
password: prisma
我会添加一些数据作为测试用。
INSERT INTO Todo(title) values
('簡単なタスク'),
('気持ち簡単なタスク'),
('ヤバいタスク'),
('気づかないフリをしているタスク');
将返回DB数据的实现更改。
由于客户端库被自动生成在 src/generated/prisma-client 中,因此我们将使用它来访问数据库。
import { prisma } from './generated/prisma-client'
...
const resolvers = {
Query: {
todo: (_: any, { id }: { id: number}) => {
return prisma.todo({ id });
},
todoes: (_: any, { title }: { title: string }) => {
return prisma.todoes({ where: { title_contains: title }});
},
},
};
将模式定义剥离到另一个文件中
由于使用模板字面量编写架构定义会很难阅读且维护性差,因此将其拆分到另一个文件中。
安装 graphql-import
Urigo/graphql-import 是一个能够实现导入GraphQL模式定义文件的库。
$ yarn add graphql-import
将模式定义拆分为多个部分。
我会创建一个新目录来存放模式定义。
$ mkdir src/schema
type Todo {
id: Int!
title: String
}
# import Todo from "todoes.graphql"
type Query {
todo(id: Int): Todo
todoes(title: String): [Todo]
}
导入架构定义
我感到非常清爽。
import express from 'express';
import { ApolloServer, gql } from 'apollo-server-express';
import { prisma } from './generated/prisma-client';
import { importSchema } from 'graphql-import';
const typeDefs = importSchema(`${__dirname}/schema/schema.graphql`);
const resolvers = {
Query: {
todo: (_: any, { id }: { id: number}) => {
return prisma.todo({ id });
},
todoes: (_: any, { title }: { title: string }) => {
return prisma.todoes({ where: { title_contains: title }});
},
},
};
const server = new ApolloServer( { typeDefs, resolvers });
结束
对于初次接触来说,整体实现似乎与 REST API 意外地相似。
不过,一旦开始全面地实施,可能会出现各种重大差异。
我很高兴终于理解了GraphQL服务器实现的概要,之前一直对此感到困惑。
如果使用prisma-labs/graphqlgen库,可以从模式定义自动生成解析器?因此,期望可以通过正确使用prisma来实现从模式定义为基础的新开发体验,所以想要尝试更多。