Idiomatic way to load lists of entities without associations with Ecto

Hello,

I use Ecto and Jason (in a Phoenix project but this is not related to Phoenix). I would like to display all my entities as JSON. This is my code :

  def index(conn, _params) do
    things = MyContext.list_things()
      |> Enum.map(&Map.drop(&1, [:__struct__, :__meta__, :some_association]))
    render(conn, "index.json", things: things)
  end

Is there a better way to do that ?

This is the @derive for Jason :

  @derive {Jason.Encoder, except: [:__meta__, :__struct__]} 

I do not want to filter out some_association because when I want to encode a single entity, I will load the association.

So, what I would like is a system with Jason that remove the fields from the entities structs when it encounters a NotLoaded struct. How to do that ?

Thank you

You can implement Jason.Encoder on your own for your types, ex.:

defimpl Jason.Encoder, for: MySchema do
  # …
end

And there filter out all fields that you do not want (or replace their value with something completely different.

1 Like

I think you could also implement Jason.Encoder for Ecto.Association.NotLoaded, as long as this is an application and not a library others will use.

Well if it is the only solution then I’ll do that. I was hoping Ecto/Jason had figured that out.

This could work in a way but the data would still be present in the parent record !

It doesn’t, because this is presentation layer responsibility, not data layer (Ecto) or just (de)serializer (Jason). If you want to handle missing data, then do it on your own.

Yes it would be and probably the best way to represent that in a JSON object is by not including the key at all. That solution requires the custom instance on your Ecto model, or if you are using Phoenix just do it the recommended way that you see in phx.gen.json: render functions that map your schema struct records to naked maps.

Well I’ll stick with Map.drop for now, thank you gentlemen :slight_smile:

In general the advice is to use Map.take and not Map.drop because Map.take is much much safer. If you add a new field that contains sensitive information to your schema, the Map.drop approach makes that information available by default. This is very bad. Instead be conservative and list the things you want to serialize.

3 Likes

My data is public but I will keep that in mind ! Thank you.