Is there an equivalent to register_after_send?

In looking do a simple, but potentially slow task after the response has been delivered to the end user, I am looking for some lifecycle hook to trigger once the response is over.

In googling this I see references to “register_before_send” – but in fact I want to execute this code after send.

I see some contradictory older threads that look somewhat complicated involving task and/or task supervisor, but it’s not clear those are relevant to the current state of phoenix.

So: what is the best practice for doing work after a response has been successfully sent?

I was imagining there would be a straightforward way to add a post-processing plug or a post-action callback.

I’m not aware of away to do this with plug, but Phoenix does emit telemetry events. You could subscribe to a telemetry [:phoenix, :endpoint, :stop] event and execute your task then.

You can decide on your own when exactly in the plug pipeline you send the response (using Plug.Conn.send_resp/1 or any code calling it). E.g. in a phoenix app that would happen as part of Phoenix.Controller.render/3. Sending a response doesn’t stop the plug pipeline though. You can have further plugs after that. You can even implement a similar callback registration to register_before_send if you want to.

If you don’t manually call Plug.Conn.send_resp will be called by plug at the end of a plug pipeline – expecting a response to be set on the conn.

1 Like

Thanks for this tip; however, it appears to me that all plugs execute before the controller; so, I guess what I am going to try is having the controller call the send_resp, and then hand off to post response code.

This is a pretty typical setup for an api server with a router, in which the router opens with “pipe_through”

1 Like

Additionally, when calling send_resp directly, everything works from the client’s perspective, but the Phoenix app reports an error:

[error] ** (Plug.Conn.NotSentError) a response was neither set nor sent from the connection

In the documentation for Conn send_resp, it notes that one should call halt/1, but neither doing this nor not doing this prevent the error.

You could plug a plug after the router (in endpoint.ex).

That error would suggest you did not call send_resp (at all), so not sure what’s up with that.

2 Likes

Right, but the response is being delivered to the client.

Update – the answer to that particular one is: I wasn’t returning the modified new conn, which probably gets marked with “response sent” for downstream record keeping.

Nonetheless, let me redirect to putting the plug in endpoint.ex instead of the pipeline, which would be the more general solution if I can make it work!

Final note: this IS the answer –

Adding the plug in endpoint.ex after the router.

This was way easier than I feared… and way harder to figure out than I expected. But I am still pretty new to Phoenix.