reitit

miwal 2024-08-04T05:26:54.233229Z

Hi everyone, -- some beginner questions! -- Conceptual question (I'm just starting with reitit) - I've used an existing app which uses compojure as a model and got a basic example working using reitit.ring/router & reitit.ring/ring-handler). I see that reitit.ring/router is "a higher order router adding support for request method, handlers and middleware", and I note this means defining all of those things as data in the route defs. i.e. middleware defined as data alongside the routes, in contrast to the middleware stack applied to the whole router at once (commonly using the thread first macro), which we are familiar with in basic ring/compojure usage. So with that router, I then pass it to the reitit.ring/ring-handler, which, according to the docs "will return a valid ring handler". But my base assumption is that "a valid ring handler" is just a plain clojure function (allbeit conforming to the ring spec, i.e. receiving a request map and returning a response map). So what does it mean to say that ring-handler "returns a valid ring handler", if the latter is just a clojure function? I'm not clear what ring-handler's purpose is or what it's doing. Had a look at the source, but still not obvious to me.

valtteri 2024-08-04T12:22:22.565689Z

ring-router returns a Router that is not a valid ring handler (clojure function). ring-handler returns a clojure function that can work as a ring-handler, using the ring-router to route the requests efficiently.

miwal 2024-08-04T05:47:50.975669Z

Conceptual question #2 - comparing reitit.core and reitit.ring, and the fact that support for handlers, middleware and request methods are added by reitit.ring (thereby not present reitit.core), is that because none of those three ideas are applicable to a router when used from ClojureScript? (I have limited experience with front end routing so far). thanks :)

miwal 2024-08-04T10:54:12.209109Z

just sharing thought process here, but does the ring spec say or imply that middleware only wraps the top level handler ('app'), and not individual handlers or groups of handlers? with reitit's ability to state middleware as data in route definitions (and presumably also at the top level) mean that middleware is indeed now applied only to some handlers and not others?

valtteri 2024-08-04T12:25:32.169699Z

Yes, reitit supports applying middleware only to certain routes. This improves performance and ergonomics, but you have to think it a bit differently from the traditional function chaining. The traditional way can also be used with reitit when/if needed, by wrapping the function that ring-handler returns with your middleware.

miwal 2024-08-04T11:17:53.708749Z

So essentially if reitit-ring/router is not something I can use on its own, and I must always convert it to a handler by wrapping it with ring-handler, why is router itself exposed to the consumer at all? Also when I add middleware as options to ring-handler, is that identical in effect to wrapping the resulting handler, as traditionally done with ring middleware & thread-first? Just trying to smash this down to the simplest possible example.

valtteri 2024-08-04T12:29:43.453969Z

You can use the router directly, but note that it’s something that implements a reitit.core/Router https://github.com/metosin/reitit/blob/master/modules/reitit-core/src/reitit/core.cljc#L42-L49. For “high-level” webapp building you probably want to use the provided helpers and abstractions and not the low-level building blocks.

miwal 2024-08-04T11:19:26.287129Z

When I try to pass a reitit-ring/router directly to the jetty adapter, that fails, it says "expects a function, you gave me an object" (if i interpret the error message correctly).

valtteri 2024-08-04T12:19:41.445099Z

Hi @michaelkw, reitit is a relatively “low level” library that contains utilities for building routers for any need. That’s why there are many smaller pieces that can be assembled together. reitit.core contains just the “routing” part and doesn’t care about other concerns too much. Ring is a specification for handling HTTP requests and reitit.ring implements that specification with all kinds of options that you can fine-tune.

miwal 2024-08-04T12:27:58.663909Z

Thanks @valtteri, so exploring further, (and you've just answered this in part already, in a comment above) I can do -

(def app
  (-> ["/mail-settings"
       {:get mail-settings :post mail-settings}]
      ring/router          ; reitit router is a great big object
      ring/ring-handler    ; but for ring it must be a function
      (wrap-defaults site-defaults)))  ; ring middleware in traditional way, wrapping all routes
But generally we would change the style by which middleware is applied, and not do it in ring/compojure style as above any more?

valtteri 2024-08-04T12:31:01.395819Z

It’s up to you. 🙂 Letting reitit handle the middleware gives you better performance and control over when/how each middleware is applied.

valtteri 2024-08-04T12:34:44.418549Z

Note, that reitit supports applying middleware on both router and route levels

valtteri 2024-08-04T12:35:55.368419Z

Here’s an example where the middleware is configured on the router level, so it applies to all routes https://github.com/metosin/reitit/blob/master/examples/ring-malli-swagger/src/example/server.clj#L133-L151 You can still define route-specific middleware in the route-data.

valtteri 2024-08-04T12:37:02.413419Z

All the options and possibilities can be overwhelming when you’re just starting. Don’t hesitate to ask more questions. 🙂

🙌 1
valtteri 2024-08-04T12:40:21.303479Z

Here’s a real-world example where both router and route level middleware are applied https://github.com/lipas-liikuntapaikat/lipas/blob/master/webapp/src/clj/lipas/backend/handler.clj

practicalli-johnny 2024-08-04T15:36:07.836009Z

@michaelkw this is a working example of using reitit and reitit ring to create an API service. The routing is configured in `router.clj https://github.com/practicalli/gameboard-donut` Middleware can be used for all routes or added directly onto a specific route

👍 1