I have this error when the client asks for an entity with an association, and I didn’t preload the association.
Example:
Client sends query document:
user {
name
group {
id
}
}
A user optionally belongs to a group.
If I return the User without preloading the group:
def create_user(attrs) do
%User{}
|> User.changeset(attrs)
|> Repo.insert()
end
The following error is returned to the client:
- locations: [{column: 11, line: 33}]
- message: “Cannot return null for non-nullable field”
- path: [“createUser”, “user”, “group”, “id”]
But with preload it will work (even when the user has no group, and thus resolves to null as well):
def create_user(attrs) do
%User{}
|> User.changeset(attrs)
|> Repo.insert()
|> case do
{:ok, user} ->
{:ok, Repo.preload(user, [:group])}
error ->
error
end
end
In the schema, there is no non_null
constraint for the group:
object :user do
field :group, :user_group
end
object :user_group do
field :id, non_null(:id)
end
Question: why without preloading do I obtain the error? group
can be null anyway from the Absinthe schema, so why does preloading matter?
When an association is not loaded the field defaults to an %Ecto.Association.NotLoaded{}
struct. So in your case the group
field of the %User{}
struct defaults to %Ecto.Association.Notloaded{}
.
Once you call Repo.preload/2
the group
field is resolved and replaced with either the associated %Group{}
struct or nil
(in case of a belongs_to association).
Then Absinthe is first going to check if the group
field is nil
in which case it will do nothing. If the field is an %Ecto.Association.Notloaded{}
struct, Absinthe will call Map.get/2
to get the value for the :id
field on that struct. However these structs have no :id
field and the Map.get/2
call is going to return nil
. But the :id
field is non-nullable, so it blows up.
I.e. Absinthe sees that there’s a map (or a struct in this case) in the group
field, and then grabs the defined object fields from it.
2 Likes
@benwilson512 if a field is a Ecto.Association.Notloaded
struct, can’t we resolve it to nil
?
Currently the resolver tries to fetch data from that struct.
Sometimes I don’t want to preload associations, for example because I know this optional association is nil
. But I still have to execute extra SQL to get rid of the Ecto.Association.Notloaded
in the struct.
I can always manually replace the Notloaded struct by nil
. But then I wondered, why not change all Notloaded associations to nil
in Absinthe 