Francesco

Francesco

Hello!

What’s the recommended best practice for dealing with the situation where the graphql object you want to return requires data that is spread over multiple DB tables and therefore schemas?

One option is just to get all the data I need from an ecto query, create a custom map and return that. However I found that sometimes returning structs and other times returning maps from my context methods makes downstream logic a little more cumbersome. For example I have the following absinthe object:

  object :block do
    field(:public_id, non_null(:string), name: "id")
    field(:content, non_null(:json))

    field :author, :user do
      resolve(&ScribeWeb.Resolvers.Accounts.get_user/3)
    end
  end

The get_user resolver then pattern matches on the parent fields. If the parent had a user_id than I will fetch that user, otherwise I will fetch the user in the resolution context:

  def get_user(%_{user_id: id}, _args, %{context: %{current_user: user}}) do
    if user.id == id do
      {:ok, user}
    else
      {:ok, Accounts.get_user(id)}
    end
  end

This pattern matches on a struct of any kind, but if I start returning maps than I need to account for that too by creating another function.

The other way I was thinking was to add an embedded schema and that I can use instead of a plain map. I don’t have any particular need for casting or validating this data though so it kind of feels like I’m using a struct for no real reason.

How has everyone else dealt with this?

Showing Posts 1 to 2

dimitarvp

dimitarvp

If you just make this change:

-  def get_user(%_{user_id: id}, _args, %{context: %{current_user: user}}) do
+  def get_user(%{user_id: id}, _args, %{context: %{current_user: user}}) do

Then your code will work just fine with both structs and arbitrary maps.

As for your original question I see no way out but use SQL JOINs (Ecto assocs). Embedded schema would work as well but it seriously depends if you want to do queries on those models or not. If you do, they shouldn’t be embedded (most of the time).

Francesco

Francesco OP

Thanks, for some reason I thought that wouldn’t match, bit silly.

Hmm, I don’t think joins would solve the problem by themselves. I can get all the data I need using joins (which I’m currently doing) and that data is returned as individual schema structs, but the final result that I want to return to the user is a combination of each of those structs. So I can make a map as follows and return that:

# Let's say my query returned three structs, %A{} = a, %B{} = b, and %C{} = c
object_to_return = %{
    id: a.id,
    owner: b.owner,
    revision: c.revision
}
{:ok, object_to_return}

This feels very not nice, as I may need the same object returned from multiple resolvers. I guess I was hoping that there was a nice pattern to take data spread across multiple tables and map that to an absinthe object.

Also I have another question! For child fields that have a resolver that needs to fetch more data depending on the parent, how do you usually deal with this? I want to avoid having a ton of very similar resolvers that only really differ by what they are expecting from the parent field. For example:

object :example do
    field :owner, :user do
      resolve(fn example, _, %{context: %{current_user: user}} ->
         {:ok, Accounts.get_user(example.user_id)}
      end)
    end
end

It feels like I will have a ton of resolvers for getting a user that only differ by what is returned from the parent. And the parent resolver than has to be sure to return the user_id (in this case), not because that is part of it’s return object but because the child resolver needs it. It just feels like this is slowly getting more and more difficult to handle and I don’t know how to keep it under control.

— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
ryanwinchester
apply_graft/2 doesn’t rewrite an add_many sub-workflow’s deps on an add step. Grafted jobs cancel with “upstream job was deleted” Version...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews