https://rk-tech.hatenablog.jp/entry/2019/11/15/230644
シェアしました。
2019-11-15
はじめに
prisma は 従来の ORM を置き換える graphql 形式の ORM で prisma 独自のスキーマを定義することにより RDBのスキーマと graphql 形式のタイプセーフな ORM api が自動生成されます。
フロントエンドの身としてはバックエンドは出来るだけ意識せずにプロダクトを作っていきたいと考えています。
「バックエンド -> graphql api -> フロントエンド まで完全にタイプセーフなコードを自動生成して爆速開発する環境」
を作ってみました。
前提事項
grqphql の知識がある方向けに書いています。
ソースコード
prisma のバージョンについて
prisma は 1系と2系があり、1系は docker 上で動くことが前提のプロダクトになります。
2系はスタンドアローンに利用できるようですが、2019.11現在まだ preview 版です。
ここでは 1系を使います。
2系はスタンドアローンに利用できるようですが、2019.11現在まだ preview 版です。
ここでは 1系を使います。
事前準備
prisma 実行クライアントの導入
$ npm install -g prisma
$ prisma -v Prisma CLI version: prisma/1.34.8 (darwin-x64) node-v10.16.3
導入できました。
docker のインストール
prisma サーバを稼動させるため docker をイントールします。具体的な導入方法はここでは割愛します。
prisma ORM サーバ構築
公式の Get Started の記事を元に環境構築を行います。
docker-compose.yml の作成
以下は PostgreSQL の例です。
version: '3' services: prisma: image: prismagraphql/prisma:1.34 restart: always ports: - '4466:4466' environment: PRISMA_CONFIG: | port: 4466 databases: default: connector: postgres host: postgres port: 5432 user: prisma password: prisma postgres: image: postgres:10.3 restart: always environment: POSTGRES_USER: prisma POSTGRES_PASSWORD: prisma volumes: - postgres:/var/lib/postgresql/data volumes: postgres: ~
prisma server の起動
docker を起動します。
$ docker-compose up -d
$ prisma init --endpoint http://localhost:4466 Created 2 new files: prisma.yml Prisma service definition datamodel.prisma GraphQL SDL-based datamodel (foundation for database)
2ファイル作成されます。
この状態ではまだデータベーススキーマは定義されていません。次のコマンドで行います。
$ prisma deploy Creating stage default for service default ✔ Deploying service `default` to stage `default` to server `local` 575ms Changes: User (Type) + Created type `User` + Created field `id` of type `ID!` + Created field `name` of type `String!` Applying changes 1.1s Your Prisma endpoint is live: HTTP: http://localhost:4466 WS: ws://localhost:4466 You can view & edit your data here: Prisma Admin: http://localhost:4466/_admin
再度 http://localhost:4466 にアクセスします。 画面右端のタブの「DOCS」を開くと、graphql api が定義されているのが分かります。
おぉ、crud 処理が一通り出来上がっている!いい感じですね。
おぉ、crud 処理が一通り出来上がっている!いい感じですね。
prisma client(ORM api) の作成
prisma は datamodel.prisma で定義された内容に従った ORM api の generate 機能を有しており prisma cli でコードを自動生成できます。
prisma init で生成された prisma.yml 設定ファイルに以下を追記します。
prisma init で生成された prisma.yml 設定ファイルに以下を追記します。
generate: - generator: typescript-client output: ./generated/prisma-client/
生成コマンドを実行します。
$ prisma generate Generating schema 17ms Saving Prisma Client (TypeScript) at /path/to/project/prisma-practice/generated/prisma-client/
output で指定した場所に2ファイル作成されます。
Graphql バックエンドサーバの構築
エコシステムライブラリ
https://nexus.js.org/
まず nexus の役割はソースコードでバックエンドサーバの graqphql タイプを定義するためのものです。 これを利用するとスキーマ定義だけでなく、合わせて TypeScript の型定義ファイルが自動生成されます。 nexus を単体で使う場合は、スキーマ定義のためのソースコードを自身で書く必要がありますが nexus-prisma ライブラリを利用することにより、その手間を軽減できます。
まず nexus の役割はソースコードでバックエンドサーバの graqphql タイプを定義するためのものです。 これを利用するとスキーマ定義だけでなく、合わせて TypeScript の型定義ファイルが自動生成されます。 nexus を単体で使う場合は、スキーマ定義のためのソースコードを自身で書く必要がありますが nexus-prisma ライブラリを利用することにより、その手間を軽減できます。
バックエンドサーバに必要なライブラリをインストールします。
npm init npm install --save graphql-yoga nexus graphql nexus-prisma npm install --save-dev ts-node-dev
nexus 向けのスキーマソースコードを
生成元になるデータは prisma generate で生成された
nexus-prisma-generate
コマンドで自動生成します。生成元になるデータは prisma generate で生成された
./generated/prisma-client
です。prisma.yml
には prisma deploy コマンドのhooks
プロパティがあるので、生成コマンドを一連の処理にしていきます。hooks: post-deploy: - prisma generate - npx nexus-prisma-generate --client ./generated/prisma-client --output ./generated/nexus-prisma
この hooks 設定を行うことで処理の流れは以下のようになります。
(prisma deploy) ->
(prisma generate) ->
(nexus-prisma-generate) ->
datamodel.prisma
->(prisma deploy) ->
(prisma generate) ->
./generated/prisma-client
->(nexus-prisma-generate) ->
./generated/nexus-prisma
やってみます。
$ prisma deploy Deploying service `default` to stage `default` to server `local` 139ms Service is already up to date. post-deploy: Generating schema... 14ms Saving Prisma Client (TypeScript) at /path/to/project/prisma-practice/generated/prisma-client/ Running prisma generate ✔ Running npx nexus-prisma-generate --client ./generated/prisma-client --output ./generated/nexus-prisma... Types generated at ./generated/nexus-prisma Running npx nexus-prisma-generate --client ./generated/prisma-client --output ./generated/nexus-prisma ✔
生成されたコードを使ってバックエンドサーバのスキーマを設定します。
schema.ts
import * as path from 'path' import { prismaObjectType, makePrismaSchema } from 'nexus-prisma' import datamodelInfo from './generated/nexus-prisma' import { prisma } from './generated/prisma-client' const Query = prismaObjectType({ name: 'Query', definition (t) { t.prismaFields(['*']) }, }) const Mutation = prismaObjectType({ name: 'Mutation', definition (t) { t.prismaFields(['*']) }, }) const schema = makePrismaSchema({ types: [Query, Mutation], prisma: { datamodelInfo, client: prisma, }, outputs: { schema: path.join(__dirname, './generated/schema.graphql'), typegen: path.join(__dirname, './generated/nexus.ts'), }, }) export default schema
ポイントはこの部分です。
t.prismaFields(['*'])
prisma deploy で生成した ORM api は全ての CRUD 処理が実装されています。
実際の grqphql サーバを作るときはユーザ権限等を考慮して公開する api を制限したりすると思いますが、この prismaFields の引数に 公開したいものだけを指定することでその制限を行う仕組みになっています。
実際の grqphql サーバを作るときはユーザ権限等を考慮して公開する api を制限したりすると思いますが、この prismaFields の引数に 公開したいものだけを指定することでその制限を行う仕組みになっています。
上の例では
*
になっているので全て公開するという意味になります。
公開設定の具体例については、公式の以下を参考にしてください。 https://www.prisma.io/docs/1.28/get-started/03-build-graphql-servers-with-prisma-TYPESCRIPT-t201/#implement-graphql-api-based-on-crud-building-blocks
最後に graphql サーバの起動スクリプトを用意します。
index.ts
import { GraphQLServer } from 'graphql-yoga' import { prisma } from './generated/prisma-client' import schema from './schema' const server = new GraphQLServer({ schema, // 1つ手前で作った schema.ts を食わせる context: { prisma }, // prisma orm api にアクセスするために context に追加 }) server.start(() => console.log('Server is running on http://localhost:4000'))
{ "scripts": { ... "dev": "ts-node-dev --no-notify --respawn --transpileOnly ./index.ts" }, }
$ npm run dev > prisma-practice@1.0.0 dev /path/to/project/prisma-practice > ts-node-dev --no-notify --respawn --transpileOnly ./ Using ts-node version 7.0.1, typescript version 3.7.2 Server is running on http://localhost:4000
http://localhost:4000 にアクセスすると playground が表示され、 user crud が利用できるようになっているはずです。
ここまでで、ほぼソースコードを書かずに graphql api が構築できました。
ここまでで、ほぼソースコードを書かずに graphql api が構築できました。
Graphql クライアントの実装
残りはクライアント側です。
graphql-codegen
ライブラリを使って クライアントコードを自動生成します。ジェネレータの設定
$ npm install --save @graphql-codegen/cli
$ graphql-codegen init
対話形式で進めるとコード生成設定ファイル(
codege.yml
)が出力されます。
? What type of application are you building
今回は framework は利用しないので、
今回は framework は利用しないので、
Application built with other framework or vanilla JS
を選択します。
? Where is your schema?
生成元になるスキーマファイルを選択します。
生成元になるスキーマファイルを選択します。
schema.ts
内の outputs.schema
で指定されている./generated/schema.graphql
を指定します。
? Where are your operations and fragments?
graphql query ファイルを配置するディレクトリを指定します。
graphql query ファイルを配置するディレクトリを指定します。
client/gql/**/*.graphql
? Where to write the output:
生成先を指定します。
生成先を指定します。
client/generated/graphql.ts
? Do you want to generate an introspection file?
Yes
Yes
? How to name the config file
デフォルトの
デフォルトの
codegen.yml
でOKです。overwrite: true schema: "generated/schema.graphql" documents: "client/gql/**/*.graphql" generates: client/generated/graphql.d.ts: plugins: - typescript - typescript-operations - typescript-document-nodes - fragment-matcher
関連する plugin library が package.json に追加されるので、インストールします。
$ npm install
初期化時に設定したコード生成 script を実行します。
$ npm run gql:gen > prisma-practice@1.0.0 gql:gen /path/to/project/prisma-practice > graphql-codegen --config codegen.yml ✔ Parse configuration ✔ Generate outputs
サンプルクライアントコードの実装
apollo 関連ライブラリをインストールします
$ npm install apollo-boost graphql-tag node-fetch --save
生成したコードを利用するサンプルクライアントを実装します。
(かなり雑です)
client/sample.ts
(かなり雑です)
import { createHttpLink } from 'apollo-link-http' import { InMemoryCache } from 'apollo-cache-inmemory' import ApolloClient from 'apollo-client' import fetch from 'node-fetch'; import gql from 'graphql-tag' import { User } from './generated/graphql' const client = new ApolloClient({ link: createHttpLink({ uri: 'http://localhost:4000', fetch: fetch, }), cache: new InMemoryCache(), }) client.query({ query: gql` query { users { id name } } `, }) .then(result => { const { users } : { users: User[] } = result.data users.forEach(user => { console.log('user.id:', user.id) console.log('user.name:', user.name) }) }) .catch(error => console.error(error));
生成された型情報を利用します。 型補完が効いていることが確認できると思います。
import { User } from './generated/graphql' ... const { users } : { users: User[] } = result.data users.forEach(user => { console.log('user.id:', user.id) console.log('user.name:', user.name) })
ここまでの操作を一連の処理にするためクライアントの自動生成処理を prisma hooks に追加します。
hooks: post-deploy: - prisma generate - npx nexus-prisma-generate --client ./generated/prisma-client --output ./generated/nexus-prisma # 以下を追加 - ts-node schema.ts - yarn gql:gen
これで準備が整いました。
prisma schema 駆動の味見
user モデルに 年齢(
age
)を追加することにします。
1.
datamodel.prisma
の User type に追加します。type User { id: ID! @id name: String! # 追加 age: Int }
2.マイグレーションを実行します。
$ prisma deploy Deploying service `default` to stage `default` to server `local` 148ms Changes: User (Type) + Created field `age` of type `Int` Applying changes 1.1s post-deploy: Generating schema... 13ms Saving Prisma Client (TypeScript) at /path/to/project/prisma-practice/generated/prisma-client/ Running prisma generate ✔ npx: installed 91 in 4.959s Running npx nexus-prisma-generate --client ./generated/prisma-client --output ./generated/nexus-prisma... Types generated at ./generated/nexus-prisma Running npx nexus-prisma-generate --client ./generated/prisma-client --output ./generated/nexus-prisma ✔ Running ts-node schema.ts ✔ yarn run v1.17.3 $ graphql-codegen --config codegen.yml [22:04:19] Parse configuration [started] [22:04:19] Parse configuration [completed] [22:04:19] Generate outputs [started] [22:04:19] Generate client/generated/graphql.d.ts [started] [22:04:19] Generate ./graphql.schema.json [started] [22:04:19] Load GraphQL schemas [started] [22:04:19] Load GraphQL schemas [started] [22:04:19] Load GraphQL schemas [completed] [22:04:19] Load GraphQL schemas [completed] [22:04:19] Load GraphQL documents [started] [22:04:19] Load GraphQL documents [started] [22:04:19] Load GraphQL documents [completed] [22:04:19] Load GraphQL documents [completed] [22:04:19] Generate [started] [22:04:19] Generate [started] [22:04:19] Generate [completed] [22:04:19] Generate [completed] [22:04:19] Generate client/generated/graphql.d.ts [completed] [22:04:19] Generate ./graphql.schema.json [completed] [22:04:19] Generate outputs [completed] Done in 0.70s. Running yarn gql:gen ✔ Warning: The `prisma generate` command was executed twice. Since Prisma 1.31, the Prisma client is generated automatically after running `prisma deploy`. It is not necessary to generate it via a `post-deploy` hook any more, you can therefore remove the hook if you do not need it otherwise. Generating schema 17ms Saving Prisma Client (TypeScript) at /path/to/project/prisma-practice/generated/prisma-client/ Your Prisma endpoint is live: HTTP: http://localhost:4466 WS: ws://localhost:4466 You can view & edit your data here: Prisma Admin: http://localhost:4466/_admin
2オペで database, orm, graphql api, client api 全てに age に関するコードが自動で追加されます。
所感
これまで自前で graphql api をゴリゴリ書いていた自分としてはめちゃくちゃ楽なので気に入りました。
個人開発やちょっとしたものを作るのにはぴったりな気がします。
個人開発やちょっとしたものを作るのにはぴったりな気がします。
0 コメント:
コメントを投稿