Circular references in Absinthe

(Elixir 1.7.4, Phoenix 1.4.0, Absinthe 1.4.13)

I’ll start off by saying I’m completely new to the Elixir ecosystem, but would love to learn it better so I can start building API’s for web/mobile apps. I found absinthe to be exactly what I was looking for to serve this purpose, but am running into some trouble in following tutorials.

Basically, I’m trying to build a circular reference between 2 objects in Absinthe (I’m not sure if this is poor practice, but from the tutorial it looks like this should be possible regardless), and I have the objects set up as follows:

defmodule MyAppWeb.Schema.Types do
  use Absinthe.Schema.Notation
  use Absinthe.Ecto, repo: MyApp.Repo

  object :user do
    field :id, :id
    field :name, :string
    field :email, :string
    field :posts, list_of(:post), resolve: assoc(:posts)
  end

  object :post do
    field :id, :id
    field :title, :string
    field :body, :string
    field :user, :user, resolve: assoc(:user)
  end
end

So essentially, I want to be able to run the following query to return a user id:

{
  users{
    posts {
      title,
      user{
        id
      }
    }
  }
}

Now, when I run my query, I receive the following output:

{
  "data": {
    "users": [
      {
        "posts": [
          {
            "user": null,
            "title": "Aut ut perspiciatis ut eaque totam voluptatem illo."
          },
          {
            "user": null,
            "title": "Amet et animi error!"
          },
          {
            "user": null,
            "title": "Qui necessitatibus qui consequatur eos quia eaque?"
          }
        ]
      }

The big thing being, my “user” values keep registering as “null”. Now, I can retrieve the user id by calling:

{ ... users{
        id
        posts { ...

But for some reason I can’t call users in posts without getting null.

Not sure if this helps, but I’ll post my schema and resolvers below (in case the problem happens to be there)

schema.ex

defmodule MyAppWeb.Schema do
  use Absinthe.Schema
  import_types MyAppWeb.Schema.Types

  query do

    field :posts, list_of(:post) do
      resolve &MyAppWeb.PostResolver.all/2
    end

    field :users, list_of(:user) do
      resolve &MyAppWeb.UserResolver.all/2
    end
  end
end

resolvers/post_resolver.ex

defmodule MyAppWeb.PostResolver do
  alias MyApp.Repo
  alias MyApp.Post

  def all(_args, _info) do
    {:ok, Repo.all(Post)}
  end
end

resolvers/user_resolver.ex

defmodule MyAppWeb.UserResolver do
  alias MyApp.Repo
  alias MyApp.User

  def all(_args, _info) do
    {:ok, Repo.all(User)}
  end
end

My questions are, is this possible? And to follow that, is this considered poor practice if it is possible? Any help would be greatly appreciated, apologies if this is the wrong place to post.


edit: addressed formatting issue

You shoul show your absinthe post object. It would be easier :slight_smile:

Will do, thanks! Post object is include in the first codeblock, but I’ll include my /post.ex file below:

defmodule MyApp.Post do
  use Ecto.Schema
  import Ecto.Changeset


  schema "posts" do
    field :body, :string
    field :title, :string
    field :user_id, :id

    timestamps()
  end

  @doc false
  def changeset(post, attrs) do
    post
    |> cast(attrs, [:title, :body])
    |> validate_required([:title, :body])
  end
end

I would expect something like this in your post object…

field :user, :user, resolve: assoc(:user)

EDIT: Sorry, You have it…

But You don’t have association defined in your schema ?!

You should replace in your post schema.

field :user_id, :id

by

belongs_to :user, User

assuming You add an alias to User.

3 Likes

Excellent, that was it, thank you! I had to add a small modification by using MyApp.User instead of just User, but this was exactly the solution I was looking for. Thank you for the help!

1 Like

That is why I assumed you add an alias :slight_smile:

alias MyApp.User

Then, You can use User as a shortcut. It’s even more interesting when You have deep nested names

Ah, I see. I’ll keep this in mind moving forward. Thanks!

1 Like