APIのリゾルバ部分を実装していきます。現在リゾルバ用のコードは以下のように自動生成された状態になっていると思います。node"))}この中身がpanicになっているメソッドの中身を、きちんと「DBに接続して処理を実行し、レスポンスに含めたい情報を戻り値にする」というように書き換えていきましょう。データを格納するDBの準備まずはDBの準備をしましょう。DBをセットアップするためにDockerを用意して......という複雑な手順にすると本題のGraphQLの内容から逸れていくため、今回は手軽さ重視でSQLiteを使ってやっていきます。以下のようなセットアップスクリプトを書きました。'REPO_1')\;"このセットアップスクリプトを実行することで、SQLiteの準備は完了です。./setup.shうまくいっていればディレクトリ直下にSQLiteのDBであるmygraphql.dbができているはずです。SQLBoilerのセットアップ標準パッケージのdatabase/sqlを用いてDBにクエリを発行してデータを取得・挿入する処理を愚直に書いても良いのですが、今回は手軽にDBを扱うためにORMツールを使ってみたいと思います。Goで使えるORMツールは複数種類ありますが、今回はその中でもSQLBoilerというものを使ってみたいと思います。SQLBoilerは事前に用意したDBスキーマからORMコードを自動生成させるタイプのORMツールです。DBスキーマがわかった状態からコードを生成しているため、リフレクションのような複雑な型マッピングを用いる必要がなく、結果的に読みやすいコードが生成される印象です。読みやすいコードが生成されるということは、自動生成のコードがいわゆるブラックボックスのようになってしまうことを防ぐことができます。「最悪自動生成コードを読めば何やってるのかわかるでしょ」という状態になっているのは、開発者目線ではとても安心感があるため、個人的には好きなORMツールです。installコマンドを用いて、sqlboilerコマンドとSQLite3用のドライバをインストールしましょう。v4.14.0SQLBoilerでORMコードを生成するためには、sqlboilerコマンドを実行することになります。そのコード生成の際のconfigをsqlboiler.tomlというファイルに記述し、レポジトリトップに配置します。"./mygraphql.db"今回の設定内容は以下のようになっています。SQLite3のDBファイルの場所ORMコードを自動生成それでは、ここまでの設定内容でORMコードを生成させてみましょう。今回はSQLite3用のコードにしたいので、以下のコマンドを実行します。sqlite3すると、graph/dbディレクトリ以下にORMコードが自動生成されます。今後はこの中に用意された関数を用いて、SQLite3のテーブルにアクセスするコードを書いていきます。go.sumサービス層の作成SQLBoilerで生成されたコードを使って、GraphQLのレスポンスを作るために必要なDB操作を行うサービス層を作っていきましょう。今回はuserクエリを例に説明していきたいと思います。@isAuthenticated}userクエリの場合には、最終的には以下のリゾルバの中身を作り上げることが目標となります。該当の名前を持つユーザー情報をクエリで探し出しmodel.User型に整形するというロジックが必要になります。この部分をサービス層に実装していきましょう。servicesパッケージの作成まずはサービス層用に新しいパッケージを作成するために、servicesディレクトリを作ります。go.sumユーザーサービス構造体の作成ユーザーオブジェクトの情報は、SQLite3のusersテーブル内に格納されています。そのため、usersテーブルに関する内容を扱うユーザーサービスを作ります。boil.ContextExecutor}SQLBoilerでは、DBにアクセスするインターフェースを表現するためにboil.ContextExecutorというインターフェース型が用意されています。標準パッケージのdb.DB型はこのインターフェースを満たしているため、boil.ContextExecutorインターフェースの具体型として使うことができます。「ユーザー名でusersテーブルをクエリして、情報を抜き出す」部分を作ります。先ほど定義したuserService型にGetUserByNameメソッドを追加して、そこにロジックを記述します。nil}ここで重要なのは、SQLBoilerで生成されたORMコードを呼び出して得られるのは、SQLBoilerコマンドにて自動生成されたdb.User型だということです。yaml:"-"`}しかし、最終的にこの結果をリゾルバで使うためには、gqlgenの方で生成されたmodel.User型が欲しいです。`json:"projectV2s"`}そのため、db.User型からmodel.User型に変換するconvertUser関数をサービス層の用意し利用しています。nil}ユーザーサービスの公開これまでサービス層のロジックをuserService構造体のメソッドとして記述してきました。userService構造体は非公開型なので、このままでは外部パッケージからこれらのロジックを使うことができません。userService構造体の中核をなすのは、ビジネスロジックを実装したメソッド部分だけです。そのため、userService構造体が持つメソッドのみを公開するインターフェースを作りましょう。error)}また、ここまではDBのusersテーブルを扱うユーザーサービスを作ってきました。usersテーブル以外にも様々なテーブルが存在し、それらを扱う新しいサービス構造体が出てくることが今後想定されます。そのため、それらを全て内部に含むサービス構造体・サービスインターフェースも作りましょう。issueテーブルを扱うissueServiceなど、他のサービス構造体ができたらフィールドを追加していく}リゾルバやサーバーエンドポイントといった外部からサービス層のロジックを使いたい場合には、Serviceインターフェースを満たす構造体servicesを作ってそれを使う形になります。それを容易に行うためのファクトリー関数も作っておきましょう。exec},}}サービス層を利用したリゾルバの作成サービス層を作成したことで、DBを利用したロジックをプログラムの中で使えるようになりました。ここからはサービス層のロジックをリゾルバの中で実際に呼び出すところを作っていきましょう。リゾルバ構造体にサービスをDI今回作りたいリゾルバは、実態としては*queryResolver型のメソッドという形で用意されています。error)そのため、このメソッドの中でサービス層(=services.Serviceインターフェース)を利用したいのであれば、*queryResolver型のフィールドのどこかにservices.Serviceインターフェースをセットする必要があります。queryResolver型の定義は以下のようにgqlgenによって自動生成されています。generateコマンドを実行するたびにユーザーがカスタムで定義した内容が上書きされて消えてしまう恐れがあるため、自動生成される部分であるqueryResolver型の定義を直接開発者が書き換えてしまうのは好ましいことではありません。そのため、queryResolver型に内包されているResolver構造体の方にサービス層への依存性注入(DI)を行います。generateコマンド実行では書き変わらないようになっています。そのため、リゾルバ内で使用したい依存先をカスタムで定義するのにぴったりな場所なのです。リゾルバの実装サービス層をDIしたことによって、リゾルバメソッド内でr.Srvと書くことでservice.Serviceインターフェースを参照できるようになりました。それを利用して、「inputとして与えられたユーザー名を持つユーザーを取得する」というuserクエリの内容を実現するリゾルバの中身を実装しましょう。name)}サーバーエントリポイントの改修サービス層をDIした新しいリゾルバができたので、それを利用してサーバーを起動しているエントリポイント部分もそれに応じて書き換えましょう。新しい手順は以下のようになります。nil))}!ここではuserクエリの実装のみを取り扱いましたが、余裕がある方は他のクエリ・ミューテーションも同様に実装してみてください。動作確認リゾルバの実装ができたところで、ここからは実際に作ったGraphQLサーバーを稼働させ、リクエストを送ってみましょう。サーバー稼働サーバーを稼働させるために、エントリポイントであるserver.goを実行します。APIサーバーが動くのと並行して、ローカルホストの8080番ポートにGraphQLサーバーにリクエストを送るPlaygroundも使えるようになるため、それを開きます。リクエストを送信Playgroundを開いたら、先ほど実装したuserクエリを実行するためのリクエストクエリを記述しましょう。}}このクエリを実行すると、以下のようにサーバーからレスポンスが得られるはずです。}}要改善ポイントレスポンス内容をよく見ると、idやnameといったプリミティブ型のフィールドはデータが入っているのに、projectV2オブジェクトはnullとなっており情報が得られていないことに気づく方もいるかもしれません。これは今回サービス層の中でusersテーブルのみをselectしてきており、テーブルJOINを用いてprojectsテーブルの中身を参照するといった処理を実装していないからです。nil}愚直にここでテーブルjoinの処理を書かなかったのには理由が存在します。その説明と実装修正は後続の章にて行おうと思いますので、それまでお待ちください。