凤凰1.4正式版的GraphQL编程(带有CRUD Web功能)

我是fukuoka.ex的代表piacere。
非常感谢您的阅读。

继续使用Elixir的Web框架“Phoenix”进行应用程序开发,新版本1.4。

上次我們製作了一個DB+API應用,這一次我們將試著製作一個GraphQL應用。

这次在创建GraphQL应用程序时,我参考了最近由@kobatako先生在“福冈采访接力赛”上发布的专栏文章“GraphQL for Elixir”系列。

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篇

如果你觉得内容有趣或者喜欢的话,请不要忘记点赞哦!

使用GraphQL库”Absinthe”

「Absinthe」是开发GraphQL应用程序时最流行的Elixir库。

image.png

根据Hexdoc中给出的示例,我将尝试构建应用程序。
https://hexdocs.pm/absinthe/our-first-query.html

使用Phoenix PJ创建GraphQL。

根据Hexdoc的说明,看起来需要使用Ecto模式,所以将创建具有数据库的Phoenix PJ。

另外,若在PJ名称中包含“Web”字串,由于文件夹创建和模块命名的问题,可能会出现故障,请注意不要使用(由于从错误消息判断是相当困难的,请谨慎处理)。

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

为了使用「Absinthe」,需要在mix.exs文件的「def deps do」部分进行追加(添加在:phoenix~的描述之前)。


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

安装库并创建数据库后,启动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> 
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
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
image.png
image.png

GraphQL API的配置

那么,现在开始配置使用Absinthe的GraphQL API。

首先,创建一个用于制作Absinthe文件的文件夹。

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

如同Hexdoc中所述,定义GraphQL API的模式规范。

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定义中将模式规范与解析器连接起来。

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上的描述來定義解析器。

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调用时会执行数据库读取操作。

defmodule Blog.Content do
  
  def list_posts do
    Repo.all(Post)
  end
  

最后,我们要按照Hexdoc上所写的路由信息加入。

同时保留默认的`scope “/”, BlogWeb do`代码块,并添加一个新的`scope “/api” do`代码块。

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客户端,而`Altair GraphQL Client`在Firefox和Chrome浏览器中都很方便。

Firefox版
https://addons.mozilla.org/zh-CN/firefox/addon/altair-graphql-client/

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

在这里,我将安装并试用Firefox版。

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中。


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

先按两次Ctrl+c,然后进行安装,重新启动Phoenix。

mix deps.get
iex -S mix phx.server

使用Altair GraphQL Client再次点击”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的依赖项中。

顺便提一下,Poison的最新版本是4.0,但在Ecto 3系列中只能使用Poison 3系列,因此请指定版本为3.1。


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

在按两次Ctrl+C后,安装并重新启动Phoenix。

mix deps.get
iex -S mix phx.server

我将再次使用Altair GraphQL客户端调用API。

然后,不再出现错误。

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查询吧。

image.png

刚刚输入的数据已经显示出来了。

结束

我們這次在Phoenix 1.4上運行了GraphQL庫”Absinthe”,並成功進行了數據提取。

我想试试在Phoenix 1.4正式版中合并的“Telemetry”,这个功能上次在rc版中无法确认。

附言:请点个赞,谢谢。

image.png
bannerAds