Best practices on building self-contained api clients with callbacks handling

Suppose I have to interact with a remote system:

  • it can be managed remotely through http
  • it replies asynchronously with an http request and expects a custom response body

I would like to hide interaction with it behind some wrapper module, ideally it would allow to do something like this: RemoteSystem.do_something(params, callback_fun).

I can’t find a non-dirty way to implement this in a self-contained manner. Maybe there is some design pattern I’m missing, I’d be grateful if someone could suggest a better solution.

What I have to do now in my Phoenix app is

  • create a RemoteSystem module located in the lib dir
  • create a route and a controller in the web dir that has to know the structure of a callback request and would call a RemoteSystem module to build a response body and to process the callback (pass it back to the original caller).

There is also a different service with it’s (own wrapper module implementing the same behaviour) that could be used instead that replies synchronously so when a RemoteSystem isn’t used (i.e started), it’s controller and routes have to be disabled too.

So the issue is that I can’t completely encapsulate interaction with this async remote system behind some context and it spans throughout the app. Is there a better way to do this?

There is a good solution, it is called GraphQL :slight_smile:

(At least if your goal is to hide some API call under an unified API)

1 Like


I might be misunderstanding, but unless you are ok with this service to bind on another port, you won’t be able to move it from your web component completely.

I’d probably add write a plug like RemoteSystemPlug which would accept the callback as an option.

# in your app's endpoint
plug RemoteSystemPlug, callback: fn event -> do_something(event) end

And then have the RemoteSystem invoke the callback when necessary. But I’m probably missing a lot about your actual use-case … However, in general, in my elixir apps I don’t use callbacks like this, but rather prefer message passing, usually via a pubsub, as probably might be relevant in your case, since you don’t know all senders/receivers ahead of time.

Well, this doesn’t solve the issue - encapsulating callbacks from a remote service inside a module / context dir :slight_smile:

My use case is pretty simple - there is an embedded system on another end that processes a request and makes a callback with a result. So when there is a need to interact with it you can simply call a function on a wrapper module passing a callback function, pubsub would probably be an overkill here.

Doing it with a Plug would probably be a good solution, didn’t think of that, thanks for the suggestion!