0

Plugs pipelines are an amazing way to build applications. Currently though I've only been applying pipelines to filter/format data before the request hits the controller. Is there a way to apply a pipeline to run after every view is processed? I have a JSON api that I run two data transformations on every single view render function.

  def render("app.json", %{app: app}) do
    app
    ...
    |> ApiHelpers.add_data_property
    |> ProperCase.to_camel_case
  end 

Is there a cleaner way of handling this or is this something I just need to do on every render function in my view modules?

UPDATE

As @sabiwara pointed out there's the register_before_send callback. I've tried implementing it for my use case but it seems the callback is more for logging than manipulating the response.

I've tried

  def call(conn, _opts) do
    register_before_send(conn, fn conn ->
      resp(conn, conn.status, conn.resp_body |> FormatHelpers.camelize() |> ApiHelpers.add_data_property())
    end)
  end

conn.resp_body is a list I've tried transforming it to a map but still no good.

ThreeAccents
  • 1,822
  • 2
  • 13
  • 24
  • Have you tried register_before_send? https://stackoverflow.com/questions/63478471/how-do-you-run-middleware-functions-post-response-in-phoenix-framework/63479351#63479351 – sabiwara Sep 19 '20 at 10:46
  • I hadn't prior but since you mentioned it I gave it a go. It seems the callback is more for logging than manipulating the response body. I've updated my question to show what I've tried. Thanks! – ThreeAccents Sep 19 '20 at 15:42
  • OK indeed, it cannot work because the registered callback will be invoked before sending, but after encoding the JSON body. Thanks for trying! – sabiwara Sep 21 '20 at 05:19

1 Answers1

0

One way to tranform the JSON content before it gets encoded is to define a custom encoder in the config: https://hexdocs.pm/phoenix/Phoenix.Template.html#module-format-encoders

Add in a new file:

defmodule MyJSONEncoder do
  def encode_to_iodata!(data) do
    data
    |> transform_data()
    |> Jason.encode_to_iodata!()
  end
  
  def transform_data(data) do
    # whatever transformation you need to make
    %{data: data}
  end
end

And to config.exs:

config :phoenix, :format_encoders, json: MyJSONEncoder

Not 100% sure it is the best way, and it will touch every single response, but it seems to be working fine for your use case.

sabiwara
  • 2,775
  • 6
  • 11