Upgrade to Pro — share decks privately, control downloads, hide ads and more …

GraphQL & Absinthe

GraphQL & Absinthe

Comment utiliser Absinthe pour bâtir un API GraphQL performant, fiable et facile à maintenir. Voici comment Absinthe a réussi à abstraire les problèmes "classiques" d’un API standard.

Simon Prévost

April 11, 2018
Tweet

More Decks by Simon Prévost

Other Decks in Programming

Transcript

  1. type Query { me: User } type User { id:

    ID name: String } Le serveur défini:
  2. Interface Union type Custom Scalar types Mutation Enums Validation Documentation

    Fragments Arguments Subscription Directive Client-side integration
  3. Intégration avec Phoenix def deps do [ {:absinthe, "!~> 1.4"},

    {:absinthe_plug, "!~> 1.4"} ] end plug Absinthe.Plug, schema: MyAppWeb.Schema Ajout des dépendances Ajout du module dans le Endpoint Phoenix
  4. → defmodule MyAppWeb.Schema do use Absinthe.Schema object :user do field

    :name, :string end query do field :me, :user do resolve fn _, _, _ !-> {:ok, Repo.find(User, "me")} end end end end type Query { me: User } type User { id: ID name: String } → Définition de schemas
  5. { me { name } } Root field :name, :string

    do resolve fn root, _, _ !-> {:ok, Map.get(root, :name)} end end
  6. { me { name(uppercase: true) } } Arguments type User

    { id: ID name(uppercase Boolean): String }
  7. Arguments field :name, :string do resolve fn (root, %{uppercase: true},

    _) !-> {:ok, Map.get(root, :name) !|> String.upcase} (root, _args, _) !-> {:ok, Map.get(root, :name)} end end
  8. { me { id name } } Infos field :user,

    :user do resolve fn _, _, info !-> child_fields = info !|> Absinthe.Resolution.project() !|> Enum.map(&(&1.name)) … end end
  9. defmodule MyAppWeb.Schema do use Absinthe.Schema object :user do field :name,

    :string end query do field :me, :user do resolve fn _, _, _ !-> {:ok, Repo.find(User, "me")} end end end end Resolve
  10. Solving GraphQL defmodule MyAppWeb.Schema do use Absinthe.Schema object :post do

    field :author, :author end query do field :posts, list_of(:posts) do resolve fn _, _, _ !-> {:ok, Repo.all(Post)} end end end end
  11. Solving GraphQL object :post do field :author, :author do resolve

    fn root, _, _ !-> {:ok, Repo.get(Author, root.author_id) end end end
  12. Plugins object :post do field :author, :user do resolve fn

    post, _, _ !-> batch({!__MODULE!__, :users_by_id}, post.author_id, fn batch_results !-> {:ok, Map.get(batch_results, post.author_id)} end) end end end def users_by_id(_, user_ids) do users = Repo.all from u in User, where: u.id in ^user_ids Map.new(users, fn user !-> {user.id, user} end) end
  13. Middleware field :time_consuming, :thing do resolve fn _, _, _

    !-> async(fn !-> {:ok, long_time_consuming_function()} end) end end field :time_consuming, :thing do resolve fn _, _, _ !-> task = Task.async(fn !-> {:ok, long_time_consuming_function()} end {:middleware, Elixir.Absinthe.Middleware.Async, task} end end
  14. Custom Scalar defmodule MyApp.Type.IPAddress do use Absinthe.Schema.Notation scalar :ip_address, name:

    "IPAddress" do serialize(&serialize_ip_address/1) parse(&parse_ip_address/1) end defp parse_ip_address(%Absinthe.Blueprint.Input.String{value: ip_address}) do ip_address !|> to_charlist !|> :inet.parse_address() !|> case do {:ok, _address} !-> {:ok, ip_address} {:error, _reason} !-> :error end end defp parse_ip_address(%Absinthe.Blueprint.Input.Null{}), do: {:ok, nil} defp parse_ip_address(_), do: :error defp serialize_ip_address(value), do: value end
  15. Subscription subscription do field :new_users, :user do arg :account_id, non_null(:id)

    config fn args, _info !-> {:ok, topic: args.account_id} end trigger :create_user, topic: fn user !-> user.account_id end end end