スマートキャンプのエンジニア瀧川です。
弊社では昨年からエンジニア合宿を企画していまして、今年は10月15日から17日までの2泊3日で実施しました!
合宿のテーマや全体感は別記事でまとめるかなと思いますが、3日の限られた時間で1チーム(4人)1つのプロダクトを作り、成果として発表する必要がありました。
この条件だとあまり技術的なチャレンジもできないな...と感じてはいたのですが、どうしてもチーム内でGraphQL触りたい欲求が高まってしまったので、なんとか負荷があまりかからない形で導入できないか調べて見つかったのがPrismaというツールでした!
本記事ではPrismaを試した際のメモ、Tips、所感を書いていきます!
(公式でPrisma2がアナウンスされてますが、ほぼ別物なので今回はPrisma1について書いています)
(多分最終的な成果物の進捗は、慣れ親しんだツールを使った場合とほぼ同じだったかなと思っています)
GraphQLとは
GraphQLとは、Web APIのための規格の1つで、「スキーマ定義」と「クエリ定義」によってサーバとクライアントの通信をサポートします。
「スキーマ定義」で標準化された型付きのスキーマを定義し、それに則ったクエリをクライアントから発行することで、安全で柔軟にデータ取得が行えます。
詳しくは公式ドキュメントなどお読みください。
また、GitHubが外部向けAPIとしてGraphQLを採用しているので、こちらで試すのも良いと思います。
Prismaとは
インストールし、GraphQLのスキーマ定義をするだけで、以下ができるようになる便利なツールです。
- 起動用docker-compose.yml生成
- データ管理アプリ(Prisma Admin)
- CRUDの自動生成(GraphQLサーバの実装)
- DB(MySQL/PostgreSQL)へのMigration
- Seed定義
- CRUDにアクセスするクライアント(JavaScript, TypeScript, Golang)生成
インストール
まず、以下のようにpackage.jsonを定義し、 yarn install
などで依存解決します。
package.json
{ "name": "prisma-sample", "devDependencies": { "prisma": "^1.34.8", "ts-node": "^8.4.1", "typescript": "^3.6.3" } }
初期化
Prismaはinitコマンドがあり、それにより必要なファイルを自動生成してくれます。
以下のように yarn prisma init
と実行すると、「データベースの接続先をどうするか」「データベースの種類はなにか」「Prismaサーバにアクセスするためのクライアントを生成するか」の質問がでます。
以下では、LocalのDockerでMySQLを新規で起動し、TypeScriptのクライアントを生成するようにしました。
$ yarn 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 TypeScrip t 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. Start your Prisma server: docker-compose up -d 2. Deploy your Prisma service: prisma deploy 3. Read more about Prisma server: http://bit.ly/prisma-server-overview Generating schema... 25ms Saving Prisma Client (TypeScript) at /Users/takigawa.yosuke/projects/personal/prisma-sample/generated/prisma-client/ ✨ Done in 31.93s.
実行後に以下のファイルが生成されていればOKです。
prisma.yml
- Prisma全般の設定を記述する
datamodel.prisma
- GraphQLのスキーマ定義
docker-compose.yml
- Prismaサーバ、DBなど起動用
起動
$ docker-compose up -d $ docker-compose ps Name Command State Ports ---------------------------------------------------------------------------------- prisma-sample_mysql_1 docker-entrypoint.sh Up 3306/tcp, 33060/tcp mysqld prisma-sample_prisma_1 /bin/sh -c /app/start.sh Up 0.0.0.0:4466->4466/tcp
localhost:4600/_admin
でPrisma Admin(Sequel Proみたいなデータ閲覧、アップデートなどできる)にアクセスができます。
(localhost:4600
でおなじみのGraphQLのPlaygroundにもアクセスできますがPrisma Adminのほうが使い勝手がよさそうです)
datamodel.prisma
に初期状態で User
が定義されているので以下を実行し、Prismaサーバを更新するとPrisma AdminでもUserが確認できると思います。
$ yarn prisma deploy
これでほぼ準備は完了です!
クエリ
試しにデータ定義を以下のように修正して、「Create」「Read」のクエリを作成してみようと思います。
ユーザの所属する会社を追加する想定でのスキーマ定義を追加します(Company has_many Usersですね)
ID! @id
でユニークかつインデックス追加をしてくれているようです@hogehoge
はPrismaのアノテーションでいくつか種類がありそう
- 関連定義をする際に、相互に参照定義するほうがクエリを書く際に楽そう
datamodel.prisma
type Company { id: ID! @id name: String! users: [User!]! } type User { id: ID! @id name: String! company: Company! }
※ datamodel.prisma更新後は、Prismaサーバに yarn prisma deploy
で反映してください
Mutation
試しに Companyを1つ、Userを2つ生成してみます。
mutation { createCompany(data: { name: "スマートキャンプ", users: { create: [ {name: "瀧川スマ太郎"}, {name: "瀧川スマ次郎"} ] } }) { id } }
Prisma Adminでクエリを実装すると、補完がしっかりと効いていてすばらしいですね!
createCompany
などはPrismaによって自動生成されたものです。
createCompany
の ブロック {}
はmutation成功時に返却してほしいデータを記述しています。
なので実行後のResponseは以下のように自動付与されたIDが含まれています。
{ "data": { "createCompany": { "id": "ck2q06vkc006n07980p4l1av0" } } }
Query
生成したCompanyとUserをすべて取得するクエリを作成します。
query { companies { id, name, users { id, name } } }
結果がこちらになります。
{ "data": { "companies": [ { "id": "ck2q06vkc006n07980p4l1av0", "name": "スマートキャンプ", "users": [ { "id": "ck2q06vmf006o07989z7b6e1s", "name": "瀧川スマ太郎" }, { "id": "ck2q06vmo006p0798yllvbja7", "name": "瀧川スマ次郎" } ] } ] } }
このクエリとレスポンスのわかりやすさはGraphQLのメリットの1つですよね。
ちなみにPrismaでは、データの絞り込み用の関数も組み込まれています(使い勝手は少しむずかしいですが...)
例えば「会社名がキャンプを含み、ユーザ名の後方が次郎ではない」のような条件だと以下のようになります。
query { companies(where: { name_contains: "キャンプ" }) { id, name, users(where: { name_not_ends_with: "次郎" }) { id, name } } }
このようなPrismaによって生成されているものは非常にたくさんあるので、公式ドキュメントや補完をよくみて慣れていく必要がありそうです。
Tips
Seedを実行する
以下のようにTypeScriptのPrismaクライアントを生成し、それを使った seed.ts
をts-nodeで実行してやるだけですね。
prisma.yml
generate: - generator: typescript-client output: ./generated/prisma-client/ seed: run: yarn ts-node ./seed.ts
seed.ts
import { prisma } from './generated/prisma-client' async function main() { await prisma.createCompany({ name: 'スマートキャンプ' }) } main().catch(e => console.error(e))
$ yarn prisma seed
たまにDBをResetする必要がある
スキーマ定義で関連を変更したり、Seedを2回実行などがエラーになるため、リセットする必要が出てきてしまいます。
ローカルでは以下のコマンド一発ですが、プロダクションではどのように運用するといいのでしょうか...
怖いですね
$ yarn prisma reset
所感
GraphQL自体は、処理がクライアントに寄せられて、必要なデータを都度取得できるのでやりやすさを感じました。
また、型があるので、Prismaのような自動生成するタイプのツールでも補完が効き、導入障壁は低く感じました。
なによりも今回小さなアプリケーションを作成したのもありますが、サーバサイド実装要らなくてフロント実装だけで済んだのは新しい可能性を感じられました...!
ただ複雑なデータを取得する場合、それなりにクエリが複雑になるのと、Prismaの仕様、GraphQLの仕様上うまく取得できない条件があったり(知識不足もあると思いますが)難しさも感じました。
(あと、公式ではPrismaとフロントエンドとの間にもう1階層GraphQLサーバかRest APIサーバを挟むのを想定しているようなので、そのあたりも今回は無視した難しさがありますね)
またバルクインサートできないとかパフォーマンスチューニングできない、スキーマの変更をした際にresetせざるを得なくなるなど、ツールとしての成熟度はまだまだ低いかなと感じました。
ただ、GraphQL自体伸び始めたところで、ようやくプロダクション導入もちらほら聞こえ始めたので、これから大いに飛躍すると思います。
Prisma2はまた毛色が違って、よりスモールで実用的なツールになっているようなので、そちらも今後追っていきたいなと感じました。
なにはともあれ、GraphQL、Prisma、新感覚でおもしろかったです!
合宿最高でした!