tag:crieit.net,2005:https://crieit.net/tags/GORM/feed 「GORM」の記事 - Crieit Crieitでタグ「GORM」に投稿された最近の記事 2019-12-20T17:17:45+09:00 https://crieit.net/tags/GORM/feed tag:crieit.net,2005:PublicArticle/15627 2019-12-20T16:47:14+09:00 2019-12-20T17:17:45+09:00 https://crieit.net/posts/MinQ-6-Go MinQ開発日記 (6) ローカルのGoの開発環境の構築とスキーマ・マイグレーション <h1 id="Echoによるエコー・サーバー"><a href="#Echo%E3%81%AB%E3%82%88%E3%82%8B%E3%82%A8%E3%82%B3%E3%83%BC%E3%83%BB%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC">Echoによるエコー・サーバー</a></h1> <p>とりあえずEchoサーバーを立てる. またローカルに開発環境を構築していなかったことを思い出す. やり方は<a href="https://crieit.net/posts/MinQ-3-Docker-Go">MinQ開発日記 (3) Docker上にGo開発環境を構築</a>と同じと思ったのですがGo Modulesというのがあるようです.</p> <h2 id="構成"><a href="#%E6%A7%8B%E6%88%90">構成</a></h2> <ul> <li>Echo (Webサーバー兼アプリケーション)</li> <li>MariaDB Docker Container</li> <li>React (認証画面とCRUDインターフェース)</li> </ul> <h2 id="goenvとGo Modules"><a href="#goenv%E3%81%A8Go+Modules">goenvとGo Modules</a></h2> <p>Goのバージョン1.13以降は何もしなくてもGo Modulesが使えるようです.</p> <p>その前にGoのバージョン管理ができるようにしましょう. pyenvやnodeenvのようにgoenvというのがあります. 公式の<a target="_blank" rel="nofollow noopener" href="https://github.com/syndbg/goenv/blob/master/INSTALL.md">Installation</a>ガイドを読むと大体わかります. GOENV_ROOTがgoenvバイナリのロケーションです.</p> <pre><code class="bash">export GOENV_ROOT="$HOME/.goenv" export PATH="$GOENV_ROOT/bin:$PATH" eval "$(goenv init -)" export GOPATH="$HOME/go" export PATH="$PATH:$GOPATH/bin" </code></pre> <p>goenvでインストール可能なバージョンを列挙するには以下のようにします.</p> <pre><code class="bash">goenv install - </code></pre> <p>バージョンがずらっと並ぶので, この中から一つ選んでインストールします. 最新版(2019/12/14時点)である1.13.4をインストールします.</p> <pre><code class="bash">goenv install 1.13.4 goenv global 1.13.4 </code></pre> <p>これでgoのバージョンは1.13.4です. Go Modulesは好きなところにフォルダを作れます.</p> <pre><code class="bash">cd ~/your_folder mkdir minq && cd minq go mod init </code></pre> <p>これでgo.modというファイルができました. 適当にserver.goというファイルを作ります(多分main.goの方が良い気もする).</p> <pre><code class="go">package main import ( "net/http" "github.com/labstack/echo" ) func main() { e := echo.New() e.GET("/", func(c echo.Context) error { return c.String(http.StatusOK, "Hello, World!") }) e.Logger.Fatal(e.Start(":1323")) } </code></pre> <p>minqフォルダ内部で以下を実行すると必要なパッケージ(例えばecho)なんかを勝手にダウンロードしてきて, ビルドしてくれます.</p> <pre><code class="bash">go build </code></pre> <p>minqフォルダの生成物はフォルダ名と同じになるのでこの場合はminqです.</p> <pre><code class="bash">./minq </code></pre> <p>サーバーが起動したらオッケーです. 特別依存性を指定しなくてもimportから解決してくれるっぽいのは良いですが, 反面githubのレポジトリを直接指定する必要があるのは少し面倒でもあります.</p> <h2 id="MariaDB"><a href="#MariaDB">MariaDB</a></h2> <p>brewで入れます.</p> <pre><code class="bash">brew install mariadb </code></pre> <p>起動もbrewを使います.</p> <pre><code class="bash">brew services start mariadb </code></pre> <p>起動したデータベースへアクセスします.</p> <pre><code class="bash">sudo mariadb -uroot </code></pre> <h3 id="GORMで必要になる情報"><a href="#GORM%E3%81%A7%E5%BF%85%E8%A6%81%E3%81%AB%E3%81%AA%E3%82%8B%E6%83%85%E5%A0%B1">GORMで必要になる情報</a></h3> <p>GORMからMariaDBにアクセスするには以下の情報が必要になります.</p> <ul> <li>username</li> <li>password</li> <li>IPアドレス/ホスト名</li> <li>ポート番号</li> <li>データベース名</li> </ul> <h4 id="username &amp; password"><a href="#username+%26amp%3B+password">username & password</a></h4> <p>上の例でmysqlというデータベースがあると思います. このデータベースにuserというテーブルがあります.</p> <pre><code class="SQL">SELECT user FROM mysql.user; </code></pre> <p>名前が被らないように, ここに新しいユーザーを登録します. <a target="_blank" rel="nofollow noopener" href="https://mariadb.com/kb/en/library/create-user/">CREATE USER</a>を使います.</p> <pre><code class="SQL">CREATE USER 'new_name'@'localhost' IDENTIFIED BY 'your_password'; </code></pre> <p>new_nameというユーザーにyour_passwordでアクセスできます. ホストはlocalhostです.</p> <h4 id="ポート番号"><a href="#%E3%83%9D%E3%83%BC%E3%83%88%E7%95%AA%E5%8F%B7">ポート番号</a></h4> <p>システム変数を指定すると以下のように表示できるようです.</p> <pre><code class="bash">show variables like 'port'; </code></pre> <p><a target="_blank" rel="nofollow noopener" href="https://mariadb.com/kb/en/library/show-variables/">SHOW VARIABLES</a><br /> <a target="_blank" rel="nofollow noopener" href="https://mariadb.com/kb/en/library/server-system-variables/#port">port</a></p> <p>ちなみに私の環境では3306でした(多分誰でも同じ).</p> <h4 id="データベース名"><a href="#%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E5%90%8D">データベース名</a></h4> <p>MariaDBがアクセスしたユーザー用に管理してるデータベースを表示するのは簡単です.</p> <pre><code class="bash">SHOW DATABASES; </code></pre> <p>testというテスト用のデータベースが存在するはずです. とりあえずこれを使います.</p> <pre><code class="SQL">USE test; </code></pre> <p>これでデータベースがtestに切り替わります. 後でGOMRでちゃんとスキーマが設定できたかを確認しましょう.</p> <h2 id="GORM"><a href="#GORM">GORM</a></h2> <p>GORMでtestデータベースにアクセスしてみましょう. ハンドラを作っておきます. ステータス・コードなんかは現状適当です(DBサーバーでエラーが出たらどうしたら良いのかわからないので).</p> <pre><code class="go">func accessDB(context echo.Context) error { db, err := gorm.Open("mysql", "root:@(127.0.0.1:3306)/test?charset=utf8&parseTime=true") defer db.Close() if err != nil { return context.String(http.StatusOK, err.Error()) } return context.String(http.StatusOK, "Connect to DB") } </code></pre> <p>この時点では以下のようなエラーが出力されます. これはrootユーザーでアクセスする場合sudoer出ないと実行できないからのようです.</p> <pre><code class="bash">Error 1698: Access denied for user 'root'@'localhost' </code></pre> <p><a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/37239970/connect-to-mysql-server-without-sudo">Connect to mysql server without sudo</a></p> <p>ここで上で作ったnew_nameユーザーを使いましょう(名前は適宜読み替えてください). testというデータベースがデフォルトで存在するはずなのでそれを指定しましょう.</p> <pre><code class="go">db, err := gorm.Open("mysql", "new_name:@(localhost:3306)/test?charset=utf8&parseTime=true") </code></pre> <p>該当箇所を変更してビルド&リスタートするとConnect to DBと表示されるはずです(末尾のクエリ文字列的なのは一旦無視します).</p> <pre><code class="go">e.GET("/v1/example/mariadb", accessDB) </code></pre> <h3 id="スキーマ・マイグレーション"><a href="#%E3%82%B9%E3%82%AD%E3%83%BC%E3%83%9E%E3%83%BB%E3%83%9E%E3%82%A4%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3">スキーマ・マイグレーション</a></h3> <p>データベースのスキーマの更新を行う作業をスキーマ・マイグレーション, あるいは単にマイグレーションと言うようです. ORM(Object Relational Mapping/Mapper)ではオブジェクト(Goの構造体)のフィールドからスキーマを決めるようです. つまり個々のテーブル上のレコードと構造体のインスタンスが対応関係にあるわけです.</p> <pre><code class="go">type User struct { ID int Name string } </code></pre> <p>テーブル名は自動的に雛形となる構造体名の複数形になります. この場合だとusersというテーブルがtestデータベースに追加されます.</p> <pre><code class="go"><br />if db.HasTable("users") { return context.JSON(http.StatusCreated, "User table is already existed") } db.AutoMigrate(&User{}) return context.JSON(http.StatusCreated, "Create users table") </code></pre> <p>適当なハンドラ関数を作って, エンドポイントに紐付けます. MariaDBに戻って以下を実行します.</p> <pre><code class="SQL">SHOW COLUMNS FROM users; </code></pre> <p>これでusersの構造が表示されるはずです.</p> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <p>EchoからGORMを通じてMariaDBにアクセスすることができました. GORMのチュートリアルでは利用するデータベースの操作には慣れている前提なのか少しつまづきました.</p> <h2 id="今後"><a href="#%E4%BB%8A%E5%BE%8C">今後</a></h2> <h3 id="React"><a href="#React">React</a></h3> <p>データベースを管理するCRUD用のインターフェースを作ります. <a target="_blank" rel="nofollow noopener" href="https://jaredpalmer.com/formik/docs/overview">formik</a>や<a target="_blank" rel="nofollow noopener" href="https://draftjs.org/">Draft.js</a>を使います. これはできたらPWA(Chrome Desktop)にしてネイティブ・アプリのように実行できるようにしたいです. 詳細に踏み込みたくないのでcreate-react-appを使います. またテストにはCypress.jsを使ってみたいです.</p> <h3 id="認証"><a href="#%E8%AA%8D%E8%A8%BC">認証</a></h3> <p>現状他の人は使わない前提なので, 実験的にWebAuthenticationを使おうと思っています.</p> <h3 id="Gitの導入"><a href="#Git%E3%81%AE%E5%B0%8E%E5%85%A5">Gitの導入</a></h3> <p>プロジェクトはバージョン管理をするのですが, このままだとgom.Openに指定したデータベースのパスワードが丸見えです. 色々方法があるようです. 最初はコマンドライン引数で渡せばいいのかと思ったのですが, Stackoverflowで質問してみるとダメだそうです. コマンドラインにセンシティブなデータを渡すことがそもそもタブーのようです.</p> <p><a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/59390753/what-is-a-standard-way-to-specify-password-for-mariadb-in-an-api-server">What is a standard way to specify password for MariaDB in an API server?</a></p> <h4 id="じゃあどうするのか?"><a href="#%E3%81%98%E3%82%83%E3%81%82%E3%81%A9%E3%81%86%E3%81%99%E3%82%8B%E3%81%AE%E3%81%8B%3F">じゃあどうするのか?</a></h4> <p>まずそもそも構成ファイルをgitの管理下に置かないという手があります(.gitignore). あるいは構成ファイルに.exampleのような拡張子を付け適当なパスワードを入れて本番用とは切り分けるやり方です.</p> <p><a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/a/2397905/12036118">What is the best practice for dealing with passwords in git repositories?</a></p> <p>ただこの方法はどちらかというとプロジェクト・テンプレートを公開するような用途に使った方がいい気もします. どちらの方法も認証情報は別の方法で管理する必要があります.</p> <p>もう一つは<a target="_blank" rel="nofollow noopener" href="https://www.vaultproject.io/">Vault</a>というソフトウェアを使う方法ですが, こちらはかなり大げさなようです.</p> <p><a target="_blank" rel="nofollow noopener" href="https://opensource.com/article/19/2/secrets-management-tools-git">4 secrets management tools for Git encryption</a></p> <p>今回は<a target="_blank" rel="nofollow noopener" href="https://www.agwa.name/projects/git-crypt/">git-crypt</a>や<a target="_blank" rel="nofollow noopener" href="https://git-secret.io/">git-secret</a>というのがあるようでこの辺を調べてみようと思います.</p> <p><a target="_blank" rel="nofollow noopener" href="https://techblog.bozho.net/storing-encrypted-credentials-in-git/">STORING ENCRYPTED CREDENTIALS IN GIT</a><br /> <a target="_blank" rel="nofollow noopener" href="https://qiita.com/daisukeoda/items/c6b6c36009fa3409dc39">本番用の.envを外部に一切知られずに安全にgithubで保存する方法</a><br /> <a target="_blank" rel="nofollow noopener" href="https://qiita.com/jqtype/items/9b0524baa4b7fe6dbde0">Gitリポジトリ暗号化のススメ - git-secret -<br /> Git</a></p> <h2 id="Reference"><a href="#Reference">Reference</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://qiita.com/seicode/items/9ffce10086f0646379a1">【Go】goenvを使ってGo1.13.4の環境構築</a><br /> <a target="_blank" rel="nofollow noopener" href="https://tech.opst.co.jp/2019/07/09/go-modulesも触れてみるgo入門/">Go Modulesも触れてみるGo入門</a><br /> <a target="_blank" rel="nofollow noopener" href="https://qiita.com/gorilla0513/items/27cd34433a48fc8b65db">Go言語のGORMを使ってみた①</a><br /> <a target="_blank" rel="nofollow noopener" href="http://psychedelicnekopunch.com/archives/639">Golang + GORM + MySQL でデータをやりとりする</a></p> ブレイン