Fork me on GitHub
#reitit
<
2020-10-25
>
Dave Russell15:10:37

@johnjtran thanks for the pointer to the examples! Let me know how your process goes -- I will do the same 🙂 One thing I'm curious about is an idiomatic way of handling mid-path destructuring logic like compojure's context. Since the different HTTP methods of a particular endpoint often re-use the same data out of the request, we have a habit of defining:

(context "/path/to/endpoint" {:keys [thing-from-request] :as req}
    (let-routes [processed-things (process thing-from-request)] 
        (GET ....
        (PUT ....
        (POST ....
        (DELETE ....
Which prevents a bunch of boilerplate. I know reitit allows attaching mid-path data, as well as middleware, but I haven't seen anywhere that the request is exposed for processing mid-route, perhaps because of the "match first" semantics? Is the best way to handle this by adding a middleware that adds processed-things to the request, and only applying that middleware on those routes? Thanks in advance 🙂

ikitommi16:10:30

@david.russell one of the reasons for starting to create reitit was how compojure handles the context. All the child-routes are re-created for each request, with real-life 4-levels deep context tree, basically the whole routing tree is re-created for each request. The macros help a bit - much of the computation still happens just once, but it's still… not good. Here's analysis what happens: https://www.slideshare.net/metosin/naked-performance-with-clojure#20 (slides 20-23).

ikitommi16:10:54

with reitit, you need to write a middleware/interceptor for the common things, and destructure on the handler. It’s more verbose, but hopefully, easier to understand.

ikitommi16:10:09

… or you can have a custom wrapper fn or macro to build the handler. Haven’t used in a long time, but plumbings fnk removes a lot of boilerplate from destucturing:

["/api/{id}" {:parameters {:body {:x int?, :y int?}
                           :path {:id uuid?}}
              :get (fnk [[:parameters 
                          [:body x y] 
                          [:path id]
                         [:user login]
                         session] ...)}]

ikitommi16:10:59

you could also add schema-annotations on the fnk itself and pull out the :paremeters schemas out of those. Less verbose, more magic:

["/api/{id}" {:get (fnk [[:parameters 
                          [:body x :- int?, y :- int?] 
                          [:path id :- uuid?]
                         [:user login]
                         session] ...)}]

Dave Russell17:10:19

@ikitommi thanks so much for the feedback! Given the overhead of context, I will be very curious to run some perf tests before and after a transition.. For our purposes I think a combination of the options you suggest will work fine -- a middleware to replace the logic in let-routes, insert data back into the request, and then destructure them on the handlers. For common destructures we could even re-use the same map, given the endpoint definitions are data 🙂 I haven't used fnk before and will have to check it out. Appreciate all the input!!