Where should I define functions for Ring to use per request?

I have a Ring function that was written with a lot of redundancy and want to refactor it for readability and code-quality sake. But I have learned from React to be careful about the context of my fn definitions; I don’t want them getting re-interned by the JVM every time a request is made. Or is the JVM, unlike the JSVM, able to handle these things without a hiccup?

The code looks like this:

(if (or (clojure.string/starts-with? (:uri request) "/api/docs")
	(clojure.string/starts-with? (:uri request) "/api/swagger.json")
	(clojure.string/starts-with? (:uri request) "/api/get-session-id")
	(clojure.string/starts-with? (:uri request) "/api/media/stream-media")
	(clojure.string/starts-with? (:uri request) "/api/partial-media/stream-media")
	(= (:uri request) "/api/ping"))

Is it worth refactoring into the containing let like (let [docs-route? (fn [req] (clojure.string/starts-with? (:uri req) "/api/docs"))]) or an even clearer refactor?

:paperclip: It looks like maybe you’re doing routing.

Might want to look into a library for that and lots of related functionality.

Bidi (GitHub - juxt/bidi: Bidirectional URI routing) is my personal favorite.

retit (GitHub - metosin/reitit: A fast data-driven routing library for Clojure/Script) is another popular choice.

If your question is about performance, one extra layer of function calls on the JVM is almost certainly never going to make any noticeable difference in this context.

For that particular code snippet, searching in strings with shared prefixes, a regular expression might be a good way to go; and actually could improve performance. The implementation as you’ve typed it there might go over each request URI more than once, where a regex might not.

For readability, requireing '[clojure.string :as s] would probably make the biggest improvement, to my eyes. letting a new predicate function that operates on request maps doesn’t do much, for me, I’d still just wonder what that did and have to go to the definition to find out. I guess, I’d lean more in that direction if that functionality was getting copy-pasted around, then a utility function like req->...-route? that got reused might be nice. But then, we’re back to probably we should just be using a routing library.

hth

my mistake – I should have been more clear. We are indeed using Reitit (great lib) and these are within a Ring Middleware. If it were code I wrote I might have been able to just write it all as routes. In fact, that might be the biggest improvement anyway; maybe these don’t need to be encoded as middleware since they are, after all, reasoning over URLs anyway…

In any case, though, my quest for understanding the performance characteristics of middleware still stands. In which cases might middleware perform poorly? In particular, will functions defined in the handler of a middleware be a problem?

Middleware itself is essentially a form of refactoring, so whatever you put in a handler can be put into middleware, and vice versa. The reason to use middelware at all is because it helps you to group common functionality together into a single middleware and apply that middleware to any number of routes that require that particular functionality. The only thing to be aware of in this case is to ensure that only the routes which require that particular functionality have the middleware applied to them, and no others.

In terms of what you are trying to do, you’ve just given the if block without any true or false expression. What exactly is the current context?

2 Likes

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.