LoginSignup
28
15

More than 1 year has passed since last update.

Phoenix 1.4正式版③ GraphQL編(CRUD Web付き)

Last updated at Posted at 2018-11-10

fukuoka.ex代表のpiacereです
ご覧いただいて、ありがとうございます:bow:

引き続き、ElixirのWebフレームワーク「Phoenix」の新バージョン1.4でアプリを作っていきます

前回は、DB+APIアプリを作りましたが、今回は、GraphQLアプリを作ってみます

今回、GraphQLアプリを作るにあたり、最近、「福岡インタビューリレー」でコラム公開された、@kobatako さんの「GraphQL for Elixir」シリーズも参考にさせてもらいました:blush:

https://press.forkwell.com/entry/2018/11/06/engineer_component
image.png

この連載の、前回までの記事は、以下になります
 |> Phoenix 1.4正式版① インストール編
 |> Phoenix 1.4正式版② DB+API編

あと、参考までに、rc版を試したときのコラムは、以下になります
 |> Phoenix 1.4rcを試す① アップデート紹介編
 |> Phoenix 1.4rcを試す② インストール編(不具合時の復旧もあるよ)
 |> Phoenix 1.4rcを試す③ ローカルSSL開発編(Gigalixirのオマケ付)
 |> Phoenix 1.4rcを試す④ webpack編

内容が、面白かったり、気に入ったら、「いいね」よろしくお願いします :wink:

GraphQLライブラリ「Absinthe」を使う

「Absinthe」は、ElixirでGraphQLアプリを開発する際の、最もポピュラーなライブラリです

http://absinthe-graphql.org/
image.png

こちらのHexdocで解説されている例に沿って、アプリ構築してみます
https://hexdocs.pm/absinthe/our-first-query.html

GraphQL用Phoenix PJを作成

Hexdocの説明を見ると、Ectoスキーマの利用があるようなので、DB有でPhoenix PJを作成します

なお、PJ名に「Web」という文字列を含むと、フォルダ作成やモジュール命名の都合から、不具合が出るため、入れないよう気を付けてください(エラーメッセージから判断付けるのは、かなり困難なので、ご注意を)

mix phx.new blog
 …(ファイル作成ログが続く)…
Fetch and install dependencies? [Yn] (←n、Enterを入力)
cd blog

「Absinthe」を利用するため、mix.exsの「def deps do」配下に追記します(:phoenix~の記載の上行に追加)

mix.exs

  defp deps do
    [
      { :absinthe,      "~> 1.4" }, 
      

ライブラリをインストールし、DBを作成した後、Phoenixを起動します

cd blog
mix deps.get
mix ecto.create
iex -S mix phx.server

Phoenix起動の際、以下ログが出ていれば起動成功です

…
[info] Running BlogWeb.Endpoint with cowboy 2.5.0 at http://localhost:4000
Interactive Elixir (1.7.4) - press Ctrl+C to exit (type h() ENTER for help)
iex> 

ブラウザで「 http://localhost:4000 」をアクセスすると、以下ページが表示されます
image.png

GraphQL APIが扱うデータ用CRUDを作成

Hexdocの冒頭にて、予め「Blog.Content」という名前のコンテキストに、「Post」という名前のEctoスキーマを作っておく必要があるため、下記コマンドで作成します(データ編集をラクにするため、CRUD Webで作成しておきます)

mix phx.gen.html Content Post posts title:string body:text

実行後の作業指示が示されるので、書いている通りに実施します(もう何度か行っているので慣れてきましたかねぇ)

…
Add the resource to your browser scope in lib/blog_web/router.ex:

    resources "/posts", PostController

Remember to update your repository by running migrations:

    $ mix ecto.migrate
lib/blog_web/router.ex
defmodule BlogWeb.Router do
  use BlogWeb, :router
  
  scope "/", BlogWeb do
    pipe_through :browser

    get "/", PageController, :index
    resources "/posts", PostController
  end
  

マイグレートします

mix ecto.migrate

Phoenixを起動します

iex -S mix phx.server

ブラウザで「http://localhost:4000/posts」にアクセスすると、CRUD Webページが見れます
image.png

「New Post」リンクをクリックし、入力画面を入力し、「Submit」ボタンクリックでデータ投入してください
image.png

GraphQL APIの設定

それでは、Absintheを使った、GraphQL APIの設定を行います

まず、Absinthe用のファイルを作るためのフォルダを作成します

cd lib/blog_web
mkdir schema
mkdir resolvers
cd ../..

Hexdocに書かれている通り、GraphQL APIのスキーマ仕様を定義します

lib/blog_web/schema/content_types.ex
defmodule BlogWeb.Schema.ContentTypes do
  use Absinthe.Schema.Notation

  object :post do
    field :id, :id
    field :title, :string
    field :body, :string
  end
end

次も、Hexdocに書かれている通り、GraphQL API定義に、スキーマ仕様とリゾルバを繋ぎ込みます

lib/blog_web/schema.ex
defmodule BlogWeb.Schema do
  use Absinthe.Schema
  import_types BlogWeb.Schema.ContentTypes

  alias BlogWeb.Resolvers

  query do
    @desc "Get all posts"
    field :posts, list_of(:post) do
      resolve &Resolvers.Content.list_posts/3
    end
  end
end

リゾルバも、Hexdocに書かれている通りに定義します

lib/blog_web/resolvers/content.ex
defmodule BlogWeb.Resolvers.Content do
  def list_posts(_parent, _args, _resolution) do
    {:ok, Blog.Content.list_posts()}
  end
end

このリゾルバから呼ばれている「Blog.Content.list_posts」は、上述したCRUD Webで作成されたDBアクセッサで、GraphQL API呼出時にDB読み込みを行うようになります

lib/blog/content/content.ex
defmodule Blog.Content do
  
  def list_posts do
    Repo.all(Post)
  end
  

最後に、やはりHexdocに書かれている通りのルーティングを追加します

なお、デフォルトで生成される「scope "/", BlogWeb do」ブロックは残しておき、「scope "/api" do」ブロックを追加します

lib/blog_web/router.ex
defmodule BlogWeb.Router do
  
  scope "/", BlogWeb do
    pipe_through :browser

    get "/", PageController, :index
  end

  scope "/api" do
    pipe_through :api

    forward "/graphiql", Absinthe.Plug.GraphiQL,
      schema: BlogWeb.Schema

    forward "/", Absinthe.Plug,
      schema: BlogWeb.Schema
  end
  

ここまでで、Absintheを動かす設定は完了なので、Phoenixを起動します

iex -S mix phx.server

GraphQL APIを呼び出す

GraphQL APIを叩くために、GraphQL APIクライアントが必要ですが、Firefox、Chrome共にある、「Altair GraphQL Client」が便利です

Firefox版
https://addons.mozilla.org/en-US/firefox/addon/altair-graphql-client/

Chrome版
https://chrome.google.com/webstore/detail/altair-graphql-client/flnheeellpciglgpaodhkhmapeljopja

ここでは、Firefox版をインストールして、使ってみます

「GET」に変更し、URLに「http://localhost:4000/api/」を入力して、「Send Request」ボタンをクリックします
image.png

すると、エラーで失敗するようです

image.png

コンソールを見ると、「Absinthe.Plug」の関数が定義されていない、というエラーが出ています

…
** (exit) an exception was raised:
    ** (UndefinedFunctionError) function Absinthe.Plug.init/1 is undefined (module Absinthe.Plug is not available)
        Absinthe.Plug.init([schema: BlogWeb.Schema])
        (phoenix) lib/phoenix/router/route.ex:39: Phoenix.Router.Route.call/2
        (phoenix) lib/phoenix/router.ex:275: Phoenix.Router.__call__/1
        (blog) lib/blog_web/endpoint.ex:1: BlogWeb.Endpoint.plug_builder_call/2
        (blog) lib/plug/debugger.ex:122: BlogWeb.Endpoint."call (overridable 3)"/2
        (blog) lib/blog_web/endpoint.ex:1: BlogWeb.Endpoint.call/2
        (phoenix) lib/phoenix/endpoint/cowboy2_handler.ex:34: Phoenix.Endpoint.Cowboy2Handler.init/2
        (cowboy) c:/piacere/code/blog/deps/cowboy/src/cowboy_handler.erl:41: :cowboy_handler.execute/2
        (cowboy) c:/piacere/code/blog/deps/cowboy/src/cowboy_stream_h.erl:293: :cowboy_stream_h.execute/3
        (cowboy) c:/piacere/code/blog/deps/cowboy/src/cowboy_stream_h.erl:271: :cowboy_stream_h.request_process/3
        (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3

そもそも、Absinthe.Plugは、インストールされていないため、mix.exsのdepsに追加します

mix.exs

  defp deps do
    [
      { :absinthe,      "~> 1.4" }, 
      { :absinthe_plug, "~> 1.4" }, 
      

Ctrl+cを2回押した後、インストールし、Phoenixを起動し直します

mix deps.get
iex -S mix phx.server

再度、Altair GraphQL Clientで「Send Request」をクリックします

なお、「Send Request」ボタンでリクエストが飛ばないときは、以下のボタンをクリックします
image.png

今度は、「Poison」の関数が定義されていない、というエラーが出ています

…
** (exit) an exception was raised:
    ** (UndefinedFunctionError) function Poison.decode/1 is undefined (module Poison is not available)
        Poison.decode("{}")
        (absinthe_plug) lib/absinthe/plug/request/query.ex:118: Absinthe.Plug.Request.Query.decode_variables/2
        (absinthe_plug) lib/absinthe/plug/request/query.ex:52: Absinthe.Plug.Request.Query.parse/3
        (absinthe_plug) lib/absinthe/plug/request.ex:71: Absinthe.Plug.Request.build_request/4
        (absinthe_plug) lib/absinthe/plug/request.ex:49: Absinthe.Plug.Request.parse/2
        (absinthe_plug) lib/absinthe/plug.ex:347: Absinthe.Plug.execute/2
        (absinthe_plug) lib/absinthe/plug.ex:247: Absinthe.Plug.call/2
        (phoenix) lib/phoenix/router/route.ex:39: Phoenix.Router.Route.call/2
        (phoenix) lib/phoenix/router.ex:275: Phoenix.Router.__call__/1
        (blog) lib/blog_web/endpoint.ex:1: BlogWeb.Endpoint.plug_builder_call/2
        (blog) lib/plug/debugger.ex:122: BlogWeb.Endpoint."call (overridable 3)"/2
        (blog) lib/blog_web/endpoint.ex:1: BlogWeb.Endpoint.call/2
        (phoenix) lib/phoenix/endpoint/cowboy2_handler.ex:34: Phoenix.Endpoint.Cowboy2Handler.init/2
        (cowboy) c:/piacere/code/blog/deps/cowboy/src/cowboy_handler.erl:41: :cowboy_handler.execute/2
        (cowboy) c:/piacere/code/blog/deps/cowboy/src/cowboy_stream_h.erl:293: :cowboy_stream_h.execute/3
        (cowboy) c:/piacere/code/blog/deps/cowboy/src/cowboy_stream_h.erl:271: :cowboy_stream_h.request_process/3
        (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3

Poisonもインストールされていないため、mix.exsのdepsに追加します

なお、Poisonは、最新版が4.0ですが、Ecto 3系で利用可能なのは、Poison 3系のため、3.1を指定します

mix.exs

  defp deps do
    [
      { :absinthe,      "~> 1.4" }, 
      { :absinthe_plug, "~> 1.4" }, 
      { :poison,        "~> 3.1" }, 
      

Ctrl+cを2回押した後、インストールし、Phoenixを起動し直します

mix deps.get
iex -S mix phx.server

再度、Altair GraphQL ClientでAPIを叩きます

すると、エラーが出なくなりました

「Send Request」ボタン左の「Docs」ボタンをクリックし、「RootQueryType」リンクをクリックし、「posts」リンクをクリックすると、上記で定義したスキーマ定義が返ってきていることが確認できました
image.png

コンソールも、エラーは出ておらず、Absintheのデバッグログが出力されています

iex(35)> [info] GET /api/
iex(35)> [debug] ABSINTHE schema=BlogWeb.Schema variables=%{}
---
query IntrospectionQuery {
      __schema {
        queryType { name }
        mutationType { name }
        subscriptionType { name }
        types {
          ...FullType
        }
        directives {
…

それでは、GraphQLのクエリーを送ってみましょう

Altair GraphQL Clientの左側にある入力エリア(コメントが書いてある部分)に、Hexdocの冒頭に書かれているクエリーを入力し、「Send Request」ボタンをクリックします
image.png

先ほど投入したデータが、表示されました

終わり

今回は、Phoenix 1.4でGraphQLライブラリ「Absinthe」を動かし、データ取得するところまで行いました

次回は、rc版では確認できていなかった、Phoenix 1.4正式版にマージされた「Telemetry」を試してみたいと思います

p.s.「いいね」よろしくお願いします

よろしければ、ページ左上の image.pngimage.png のクリックをお願いしますー:bow:

28
15
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
15