Use cases for the queue in interceptors?


#1

Interceptors are a generalization of the middleware pattern. They were pioneered by Pedestal and since then they’ve shown up in e.g. re-frame and reitit. I’ve been thinking about them and a while ago I wrote a post about their pros and cons.

Something I keep wondering about is that is the ability to manipulate the queue essential? I’m aware of the following use cases for web backends:

  1. Early return. For example, you might have an authorization interceptor that returns a “403 Forbidden” response if the user is not logged in. In this case you empty the queue.
  2. Routing. A routing library could look at the request and push new interceptors and a request handler to the queue. This would allow you to chain routing libraries in the same interceptor queue. I’ve chained routing libraries, for sure, but I’m not sure if they need to share the interceptor chain.
  3. Re-ordering the interceptors. You could have an interceptor that looks at the queue and re-orders it based on the dependencies between the interceptors like angel-interceptor does. I’ve actually used this with Kekkonen, but e.g. angel-interceptor is not an interceptor itself.

Now, Eric Normand’s interceptors do not have the queue exposed but they still support early return. I believe re-ordering is better done from the outside like angel-interceptor does and I’m unsure whether chained routing libraries need to share the interceptor context.

Does anyone know of other use cases for manipulating queue?


#2

Another use case would be to provide hooks into a library for its consumer to provide interceptors when they normally would not be accessible.

re-flow is a re-frame library that implements this use case. It provides an event handler for an event that will ultimately be dispatched outside the library by the application.

However, it is possible (maybe even likely) that the calling application will want to inject custom behavior to be run with the event. However, since the event handler is implemented inside the library, there is no direct way to specify an interceptor on it.

To address this issue, re-flow provides a mechanism for an application to register the interceptors it would like to run with that event. Internally, it uses a custom interceptor that simply pushes the registered interceptors onto the queue.

You can see an example of its use here.


#3

We enqueue interceptors in our pedestal app in order to perform side effects. So if an interceptor wants to write to Datomic it enqueues an interceptor for that, same for sending an email.

In contrast to the approach outlined in http://pedestal.io/reference/interceptors with a database-interceptor that’s always part of the chain, we enqueue the effectful interceptor, so we can know if a side effect was performed successfully (leave is called) or errored (error is called) and to ensure ordering (e.g. write to db first, send email only if that succeeded).

We also built Nextjournal’s code execution using pedestal’s interceptor chain. When a user presses run to execute a code cell, we look at the request and the current state and enqueue interceptors that do the necessary work (pull docker image, boot runtime, execute dependent cells etc). This is nice because it allows us to test this planning step without having to perform actual side effects.


#4

Thanks for the responses, they’re just what I was looking for! Clearly the dynamic queue has interesting use cases.


#5

We also use this feature in a big website where the URL shape doesn’t give us enough information about what to render exactly. We need to call an external database that give us all necessary information, and only then we know what to do. So after the call to the DB we enqueue the right final interceptors to render the page. Another nice usage!