#graphql #elixir #ecto #dataloader #absinthe
#graphql #эликсир #ecto #dataloader #absinthe
Вопрос:
У меня есть API GraphQL, который отлично работает с использованием обычных функций разрешения. Моя цель — устранить проблему N 1.
Для этого я решил использовать Dataloader. Я выполнил эти шаги, чтобы предположительно запустить приложение:
- Я добавил эти две функции в свой контекстный модуль:
defmodule Project.People do
# CRUD...
def data, do: Dataloader.Ecto.new(Repo, query: amp;query/2)
def query(queryable, _params) do
queryable
end
end
- Я добавил
context/1
иplugins/0
в модуль схемы и обновил преобразователи для запросов:
defmodule ProjectWeb.GraphQL.Schema do
use Absinthe.Schema
import Absinthe.Resolution.Helpers, only: [dataloader: 1]
alias ProjectWeb.GraphQL.Schema
alias Project.People
import_types(Schema.Types)
query do
@desc "Get a list of all people."
field :people, list_of(:person) do
resolve(dataloader(People))
end
# Other queries...
end
def context(context) do
loader =
Dataloader.new()
|> Dataloader.add_source(People, People.data())
Map.put(context, :loader, loader)
end
def plugins, do: [Absinthe.Middleware.Dataloader | Absinthe.Plugin.defaults()]
end
Никакие другие шаги не приведены в официальных руководствах. Мой :person
объект выглядит так:
@desc "An object that defines a person."
object :person do
field :id, :id
field :birth_date, :date
field :first_name, :string
field :last_name, :string
field :pesel, :string
field :second_name, :string
field :sex, :string
# field :addresses, list_of(:address) do
# resolve(fn parent, _, _ ->
# addresses = Project.Repo.all(Ecto.assoc(parent, :addresses))
# {:ok, addresses}
# end)
# description("List of addresses that are assigned to this person.")
# end
# field :contacts, list_of(:contact) do
# resolve(fn parent, _, _ ->
# contacts = Project.Repo.all(Ecto.assoc(parent, :contacts))
# {:ok, contacts}
# end)
# description("List of contacts that are assigned to this person.")
# end
end
Прокомментированная часть — это распознаватель, который работает без dataloader
него и не вызывает проблемы.
Когда я пытаюсь запросить:
{
people {
id
}
}
Я понимаю это:
Request: POST /graphiql
** (exit) an exception was raised:
** (Dataloader.GetError) The given atom - :people - is not a module.
This can happen if you intend to pass an Ecto struct in your call to
`dataloader/4` but pass something other than a struct.
Я не полностью понимаю сообщение об ошибке, поскольку я передаю модуль в dataloader/1
и не могу найти решение. В чем может быть дело?
Ответ №1:
Мне удалось заставить это работать — вот как:
Сам dataloader
по себе не знает, что извлекать из базы данных, он понимает только ассоциации. Таким образом dataloader(People)
, может быть только частью object
блока, а не query
блока.
Другими словами:
defmodule ProjectWeb.GraphQL.Schema do
use Absinthe.Schema
import Absinthe.Resolution.Helpers, only: [dataloader: 1]
alias ProjectWeb.GraphQL.Schema
alias Project.People
import_types(Schema.Types)
query do
@desc "Get a list of all people."
field :people, list_of(:person) do
resolve(amp;StandardPerson.resolver/2)
end
# Other queries...
end
def context(context) do
loader =
Dataloader.new()
|> Dataloader.add_source(People, People.data())
Map.put(context, :loader, loader)
end
def plugins, do: [Absinthe.Middleware.Dataloader | Absinthe.Plugin.defaults()]
end
и
@desc "An object that defines a person."
object :person do
field :id, :id
field :birth_date, :date
field :first_name, :string
field :last_name, :string
field :pesel, :string
field :second_name, :string
field :sex, :string
field :addresses, list_of(:address) do
resolve(dataloader(People))
description("List of addresses that are assigned to this person.")
end
field :contacts, list_of(:contact) do
resolve(dataloader(People))
description("List of contacts that are assigned to this person.")
end
end