Problems with pagination (Absinthe & Ecto)

Hello :slight_smile:

I’m working on a API for my company, to get GDPR related information out of our database and delete them. It was a good use case for me to get more familiar with elixir and graphql… I’ve read the book “Craft graphql APIs in Elixir”

I tried to adopt the information on my project. There is a search function, where we’re able to search for a customer’s name. But if there are hundrets of records, the api takes too much time. That’s why i want to use pagination. But I struggle. Regarding the book, I did the complete implementation. But I still have this error:

    [error] #PID<0.385.0> running ExorcistApiWeb.Endpoint (cowboy_protocol) terminated
Server: 0.0.0.0:4000 (http)
Request: POST /graphiql
** (exit) an exception was raised:
    ** (Protocol.UndefinedError) protocol Ecto.Queryable not implemented for [%ExorcistApi.Customer{__meta__: #Ecto.Schema.Metadata<:loaded, "ps_customer">, addresses: #Ecto.Association.NotLoaded<association :addresses is not loaded>, date_add: #DateTime<2017-06-21 13:18:38.000000Z>, date_upd: #DateTime<2017-06-21 13:18:38.000000Z>, email: "asdasdasimon@asdasdasdas.com", firstname: "asdasd", id_customer: 65099, id_gender: 2, id_lang: 1, lastname: "asda Simon", newsletter: 1, newsletter_date_add: #DateTime<0000-00-00 00:00:00.000000Z>, optin: 0, orders: #Ecto.Association.NotLoaded<association :orders is not loaded>, rxs: #Ecto.Association.NotLoaded<association :rxs is not loaded>}, %ExorcistApi.Customer{__meta__: #Ecto.Schema.Metadata<:loaded, "ps_customer">, addresses: #Ecto.Association.NotLoaded<association :addresses is not loaded>, date_add: #DateTime<2015-08-24 08:49:36.000000Z>, date_upd: #DateTime<2017-12-15 14:33:57.000000Z>, email: "simon@adasdas.com", firstname: "Simon", id_customer: 17107, id_gender: 1, id_lang: 1, lastname: "asdasd", newsletter: 1, newsletter_date_add: #DateTime<2016-08-17 14:44:41.000000Z>, optin: 0, orders: #Ecto.Association.NotLoaded<association :orders is not loaded>, rxs: #Ecto.Association.NotLoaded<association :rxs is not loaded>}]. This protocol is implemented for: Atom, BitString, Ecto.Query, Ecto.SubQuery, Tuple
        (ecto) deps/ecto/lib/ecto/queryable.ex:1: Ecto.Queryable.impl_for!/1
        (ecto) deps/ecto/lib/ecto/queryable.ex:9: Ecto.Queryable.to_query/1
        (ecto) lib/ecto/query/builder/limit_offset.ex:54: Ecto.Query.Builder.LimitOffset.apply/3
        (absinthe_relay) lib/absinthe/relay/connection.ex:407: Absinthe.Relay.Connection.from_query/4
        (absinthe) lib/absinthe/resolution.ex:209: Absinthe.Resolution.call/2
        (absinthe) lib/absinthe/phase/document/execution/resolution.ex:209: Absinthe.Phase.Document.Execution.Resolution.reduce_resolution/1
        (absinthe) lib/absinthe/phase/document/execution/resolution.ex:168: Absinthe.Phase.Document.Execution.Resolution.do_resolve_field/4
        (absinthe) lib/absinthe/phase/document/execution/resolution.ex:153: Absinthe.Phase.Document.Execution.Resolution.do_resolve_fields/6
        (absinthe) lib/absinthe/phase/document/execution/resolution.ex:72: Absinthe.Phase.Document.Execution.Resolution.walk_result/5
        (absinthe) lib/absinthe/phase/document/execution/resolution.ex:53: Absinthe.Phase.Document.Execution.Resolution.perform_resolution/3
        (absinthe) lib/absinthe/phase/document/execution/resolution.ex:24: Absinthe.Phase.Document.Execution.Resolution.resolve_current/3
        (absinthe) lib/absinthe/pipeline.ex:269: Absinthe.Pipeline.run_phase/3
        (absinthe_plug) lib/absinthe/plug.ex:414: Absinthe.Plug.run_query/4
        (absinthe_plug) lib/absinthe/plug.ex:240: Absinthe.Plug.call/2
        (phoenix) lib/phoenix/router/route.ex:163: Phoenix.Router.Route.forward/4
        (phoenix) lib/phoenix/router.ex:278: Phoenix.Router.__call__/1
        (exorcist_api) lib/exorcist_api_web/endpoint.ex:1: ExorcistApiWeb.Endpoint.plug_builder_call/2
        (exorcist_api) lib/plug/debugger.ex:102: ExorcistApiWeb.Endpoint."call (overridable 3)"/2
        (exorcist_api) lib/exorcist_api_web/endpoint.ex:1: ExorcistApiWeb.Endpoint.call/2
        (plug) lib/plug/adapters/cowboy/handler.ex:16: Plug.Adapters.Cowboy.Handler.upgrade/4

Link to Gitlab Repo Clone:
https://gitlab.com/birdnestman/exorcist-api-public-clone/tree/master

Putting up an example is very helpful, so thanks! However can you also include a snippet of code someone could run to produce the error you’re seeing?

Hey Ben
Thank you for the book. It helped me a lot!

Would be nice if you can run it, but I think you have to setup the same environment?

What i can provide is the Query I send.

{
customers(first: 3, filter: {name:“simon”}) {
edges {
node {
email
firstname
idCustomer
idGender
lastname
}
}
}
}

Further, I was not sure where this error happens, so I added a link to the public repo. This is the customer.ex, it could be something here:

defmodule ExorcistApi.Customer do
use Ecto.Schema
import Ecto.Changeset
import Ecto.Query, warn: false
alias ExorcistApi.{Rx, Address, Customer, Repo, Order}

@primary_key {:id_customer, :id, autogenerate: true}

schema “ps_customer” do
field :id_gender, :integer
field :id_lang, :integer
field :firstname, :string
field :lastname, :string
field :email, :string
field :newsletter, :integer
field :newsletter_date_add, :utc_datetime
field :optin, :integer
field :date_add, :utc_datetime
field :date_upd, :utc_datetime

has_many :orders, Order, foreign_key: :id_customer
has_many :addresses, Address, foreign_key: :id_customer
has_many :rxs, Rx, foreign_key: :id_customer

end

def customers_query(args) do
Enum.reduce(args, Customer, fn
{:order, order}, query →
query |> order_by({^order, :lastname})
{:filter, filter}, query →
query |> filter_with(filter)
_, query →
query
end)
|> Repo.all
end

defp filter_with(query, filter) do
Enum.reduce(filter, query, fn
{:name, name}, query →
from q in query, where: like(q.firstname, ^“%#{name}%”), or_where: like(q.lastname, ^“%#{name}%”)
{:email, email}, query →
from q in query, where: like(q.email, ^“%#{email}%”)
{:id_gender, id_gender}, query →
from q in query, where: q.id_gender == ^id_gender
{:id_customer, id_customer}, query →
from q in query, where: q.id_customer == ^id_customer
end)
end

def list_customers(_) do
Repo.all(Item)
end
end