続・Golangを使って簡単なwebバックエンドを書いた (意訳: Prismaを使ったDBスキーマ管理とGolangにおけるセッションハンドリング)
はじめに
どうも。飛び上がり自殺をしようとしたら一般気象学 第2版補訂版(著: 小倉 義光, 2016)を持った奇妙な関西弁を話す謎の生物に止められたため、今度はドリルでモホロビチッチ不連続面まで掘削し、その過程の地熱と地圧で死ねないか考えている者です。
前回、Golangで簡単なWebバックエンドを書いたの記事では、次の2点の問題について取り上げました。
- 認証周り (セッション管理)がちょーっと弱い。
- データベースのマイグレーションがDjangoやRailsのように実用性のあるものではない。
今回はこの2点を頑張って対処してみます。
セッション管理の問題
この問題ついては別途、ライブラリを作りました。👉gauth
このライブラリは「ガウス」とか「ゲウス」とか「ギャウス」とか読んであげてください。
gauthはベースとなる技術として、JWTを使っています。JWTについては調べていただくことにして、このライブラリによって、
- セッション管理
- ログインが必須なリソースの保護
ができるようになりました。めでたしめでたし
DBの問題
GolangでORMを行う場合、Gormの利用が筆頭に上がるかと思います。が、このライブラリの自動マイグレーションはフィールドの追加とテーブルの追加以外の事ができません。 例えば、gormで次の事をした場合、マイグレーションを自分で書き上げなければなりません。
- NULL許容だったフィールドをNULL非許容にした
- フィールドやテーブルをリネームした
- フィールドを消した
- すでにあるフィールドにインデックス属性をつけた
Djangoはここら辺が非常によくできていて、相当に特殊な場合を除いて、python manage.py makemigrations
を実行するだけで、これらの問題に対処するマイグレーションファイルをいい感じに書いてくれます。が、Golangではそういったいい感じのDBマイグレーションの生成支援ツールがない。
・・・ よし、作るか。
等と普段の僕なら考えるのですが、巷ではSpec-Drivenな開発手法が流行っているようです。Zノーテーション万歳!と言いたいところですが、そこまで高度な形式仕様の記述は必要としていません。欲しいのは何かしらの言語で記述されたデータモデルの仕様をDBのスキーマに落としてくれるツールだ!!しかも実用的なマイグレーションつきで!!
というわけで探したら・・・ ありました!!Prismaが!!
Prismaとは
公式サイトより引用すると、
Prisma replaces traditional ORMs
との事です。つまり、一般的なORMを置き換えることを目的としたツールという事になります。
Prismaの機能
Prismaがもつ機能として、次の機能があります:
- DBとの調停
- GraphQLベースのDSLによってモデルの仕様を書くことができる
- 実用的なマイグレーション (なお2.0ではマイグレーションのバージョニングが追加されるという。)
- DSLによって記述されたモデルデータをGolangなりTypescriptなりのクライアントとして落とし込める
- Adminパネル
・・・Djangoが使えない(というか巷ではNodeJSやGolangなどでWebバックエンドを開発することが多いそうですが)場合は間違いなくPrismaはモデル管理ツールの筆頭候補になりそうですね。素晴らしい。
使ってみる
と、いうわけでPrismaを使ってみます。
1. Prismaクライアントのインストール
クライアント自体はNPMに置かれています。と、いうわけで、おもむろにコンソールを開いて次のコマンドでクライアントをインストールします。
npm i -g prisma
2. サーバーのインストール
PrismaのサーバーはDocker化されています。つまり、docker-compose.yml
あたりで開発に必要な構成を定義して開発を行うとようにしても良いのですが、今回は単純なコードサンプルだけなので、PostgresとPrismaをdockerコンテナとして動かします:
version: '3'
services:
db:
image: postgres:alpine
restart: always
environment:
POSTGRES_PASSWORD: go-sample
POSTGRES_USER: go-sample
prisma:
image: prismagraphql/prisma:1.31
restart: always
depends_on:
- db
ports:
- "4466:4466"
environment:
PRISMA_CONFIG: |
port: 4466
databases:
default:
connector: postgres
host: db
port: 5432
user: go-sample
password: go-sample
今回、使用するサーバーのバージョンは1.31ですが、これより新しいバージョンのアプリがリリースされた場合はそれを使用するようにしてください。
Prismaのサーバーを終了するとSIGKILLが発生する件について
頑張ってPrismaとPostgresのdocker-compose.yml
を書き、さあやっと遊べますよヤッター⭐と思ったのですが、なんとdocker-composeを終了する時にPrismaが終了コードコード137を返すではありませんか!!
go-gql-sample_prisma_1 exited with code 137 <-- アッー!!
db_1 | 2019-04-28 03:24:13.906 UTC [1] LOG: received smart shutdown request
db_1 | 2019-04-28 03:24:13.907 UTC [1] LOG: background worker "logical replication launcher" (PID 24) exited with exit code 1
db_1 | 2019-04-28 03:24:13.907 UTC [19] LOG: shutting down
db_1 | 2019-04-28 03:24:13.915 UTC [1] LOG: database system is shut down
go-gql-sample_db_1 exited with code 0
この問題、まさに以前Qiitaで記事にしたことのある問題ドンピシャなのです:
と、いうわけでこの問題に対処します。 まず、原因箇所を特定するため、docker-compose ps
を実行してPrismaの中でどういったプロセスが実行されているのか調べます:
Name Command State Ports
-------------------------------------------------------------------------------------------
go-gql-sample_db_1 docker-entrypoint.sh postgres Up 5432/tcp
go-gql-sample_prisma_1 /bin/sh -c /app/start.sh Up 0.0.0.0:4466->4466/tcp
この出力では、go-gql-sample_prisma_1
がPrismaのコンテナになります。そして、このコンテナが実行しているコマンドはどうやらシェルで書かれているようですね。というわけでその中身を見てみましょう。
#!/bin/bash
set -e
/app/prerun_hook.sh
/app/bin/prisma-local # <-- これ!
はいビンゴ!つまり/app/bin/prisma-local
は新しく作成されたプロセス内で実行されます。(i.e. SIGINTが伝わってこない) そして同様に/app/bin/prisma-local
についてもここに書きたいところですが、当該のスクリプトをここに書くのは長過ぎるので、結論のみを書くと、/app/bin/prisma-local
はちゃんとexec
を使ってサーバーを起動しておりました。
というわけで、/app/start.sh
の内容を次のように書き換え、新しくDockerイメージを作成します:
#!/bin/sh -e
# -*- coding: utf-8 -*-
/app/prerun_hook.sh
exec /app/bin/prisma-local # execビルトインコマンドはプロセスを置き換える
FROM prismagraphql/prisma:1.31
COPY ./prisma-patch.sh /app/start.sh
CMD [ "/app/start.sh" ]
version: '3'
services:
db:
image: postgres:alpine
restart: always
environment:
POSTGRES_PASSWORD: go-sample
POSTGRES_USER: go-sample
prisma:
build:
context: ./
dockerfile: prismasvr.dockerfile
restart: always
depends_on:
- db
ports:
- "4466:4466"
environment:
PRISMA_CONFIG: |
port: 4466
databases:
default:
connector: postgres
host: db
port: 5432
user: go-sample
password: go-sample
上記の変更を行った後、docker-compose up
とdocker-compose stop
を行って様子を見てみましょう。
go-gql-sample_prisma_1 exited with code 143
db_1 | 2019-04-30 06:42:17.810 UTC [1] LOG: received smart shutdown request
db_1 | 2019-04-30 06:42:17.813 UTC [1] LOG: background worker "logical replication launcher" (PID 24) exited with exit code 1
db_1 | 2019-04-30 06:42:17.813 UTC [19] LOG: shutting down
db_1 | 2019-04-30 06:42:17.835 UTC [1] LOG: database system is shut down
go-gql-sample_db_1 exited with code 0
と、このように、Prismaを正常に終了させるようにする事ができました。
尚、これはバグなのでGithubにプルリクを出しています。マージされるかどうかは不明。
3. prisma.ymlの作成
Prismaには環境設定ファイルが存在し、これを以下のコマンドで簡単に作成しておきます:
prisma init --endpoint http://localhost:4466
このコマンドを実行すると、prisma.yml
とdatamodel.prisma
の2つのファイルが作成されます。前者が環境設定ファイル、後者がデータモデル定義ファイルとなります。また、データモデルの定義には複数のファイルを指定することができ、そのようにする場合はprisma.yml
を次のように書き換える必要があります。
endpoint: http://prisma:4466
datamodel:
- models/user.prisma
- models/payment.prisma
- models/etc.prisma
しかし、今回の場合、記述するべきモデルは一つだけなので、デフォルトの設定でも問題ありません。
4. データモデルの定義
さて、Prismaのバグに簡易パッチを当て、ようやくデータモデルの定義に移ることができます。先にも述べたように、Prismaのモデル定義のDSLはGraphQLのtype
に各種ディレクティブを付け加えたものになっています。
そして、今回の簡単な認証バックエンドに必要なモデルは一つ。ユーザー名とパスワードのハッシュを格納するUser
モデルのみです。これを定義します:
type User {
id: ID! @unique @id
username: String! @unique
password: String!
}
上記コードで独特な点は、@unique
と@id
で、次の効果があります。
-
@unique
はSQLで言うところのUNIQUE
制約、つまり、「保存されているデータの当該カラムの情報はユニークでなければならない」という制約です。 -
@id
はIDで型付けされたフィールドのみ有効な語句で、これが指定される事によって当該のフィールドの値はPrismaによって自動生成され、ユーザがAdminパネルで編集することはできません。
これらのディレクティブに加え、Date
型には@createdAt
等のディレクティブなども用意されています。詳細についてはヘルプをご覧頂くとして、取り敢えず上記コードでUser
モデルを定義することができました。
5. モデルの展開
次に、モデルをPrismaに展開させます。とはいえ、やることは単純に次のコマンドを実行するだけです:
prisma deploy
6. クライアントの作成
次に、バックエンドからPrismaを操作するためのクライアントを作成します。今回はGraphQLベースのバックエンドなので、作成すべきコードは次の2つです:
- GQLGenのためのGraphQLスキーマ定義
- Resolver Prisma のためのGo言語で書かれたPrismaクライアント
これらのコードを作成するには、prisma.yml
に少し変更を加えます:
endpoint: http://prisma:4466
datamodel:
- models/user.prisma
generate: # 👈👇この項目を追加する
- generator: go-client
output: ./backend/prisma
- generator: graphql-schema
output: ./backend/schemata/generated.graphql
hooks:
post-deploy:
- prisma generate # 👈 prisma deployした時に自動的にクライアントも更新させる
上記変更を行った後、prisma deploy
あるいは、prisma generate
を実行すると、./backend/prisma
にGo用クライアントが、./backend/schemata/generated.graphql
にGraphQL スキーマが生成されます。
7. ゴリゴリ書く
あとはゴリゴリとバックエンドを書くだけです
また、書いたコードはここにあります。ありがとうございました。
終わりに
とりあえず、Prismaの使い方をざっくり書いてみましたが、コードを修正してプルリク送るよりもQiitaに記事をアウトプットするほうが労力がかかるように思えました。独創的な死に方で自殺する前に考えた事をコードに落とし込むAIと考えたことをブログ記事にするAIはよ!!
では。
Author And Source
この問題について(続・Golangを使って簡単なwebバックエンドを書いた (意訳: Prismaを使ったDBスキーマ管理とGolangにおけるセッションハンドリング)), 我々は、より多くの情報をここで見つけました https://qiita.com/hyamatan/items/9e042187bc926d6f1704著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .