reitit

miwal 2024-09-11T06:49:21.483619Z

I have a question about syntax for route names. I find this does register a route name: ["/threads/create" threads.create] This doesn't: ["/threads/create" threads.create {:get (fn [req] nil)}] But this does: ["/threads/create" {:name threads.create :get (fn [req] nil)}] From the docs, it implies on my reading that the second example above should work. When I define these routes and call reitit-ring/route-names on a reitit-ring/router created with them, they don't appear.

miwal 2024-09-11T06:54:11.337439Z

Also a little discombobulated by the docs. Are the examples in the docs on route syntax real examples? It seems to me they aren't, because paths on their own don't really serve a purpose, a router routing to what? Maybe it would be better to have a full realistic example of a routing table, instead of covering successive edge cases which seem more like developer notes? I mean, a definitive list of what is and isn't legal is one way of documenting things, just saying how to achieve a real example might be as or more effective?

miwal 2024-09-11T06:56:37.102329Z

(I know there are also source examples in the repo).

opqdonut 2024-09-11T06:57:01.655869Z

which document are we talking about? this one? https://github.com/metosin/reitit/blob/master/doc/basics/route_syntax.md

miwal 2024-09-11T06:57:22.992499Z

yes

opqdonut 2024-09-11T06:58:47.175389Z

I'm not defending that document, but I'm not seeing the ["path" ::name {:foo :bar}] syntax mentioned

miwal 2024-09-12T08:02:48.890959Z

Does the service function always return the http response? (I mean the ring response map)

opqdonut 2024-09-12T08:03:24.896499Z

oh yeah that's usually outside the service function

miwal 2024-09-12T08:07:46.887829Z

i was veering toward the idea that the service function would (nearly) always return the ring response map itself; be the last thing called by the inline (anonymous) handler. unless there was some reason the anonymous handler needed to do it; after the service function returned. but i don't have examples yet.

miwal 2024-09-12T08:15:24.048689Z

So you are saying the service function usually doesn't return the http response?

opqdonut 2024-09-12T08:29:01.180619Z

Actually I think I've seen both. So the service function can return the ring response like {:status 200 :body {:name "bob" :id 123}}, or then just the domain object, {:name "bob" :id 123} and the reitit handler function then wraps this. So something like

:handler (fn [req]
           (let [id (... get param from req ...)
                 user (service/get-user id)]
             (if user
               (ok user)
               (not-found))))
             

👍 1
opqdonut 2024-09-12T08:29:20.062019Z

this latter form is nice when you want to use service/get-user in other, non-ring contexts

opqdonut 2024-09-12T08:29:36.425599Z

but there are no right answers here, it's up to you which pattern you want to use

opqdonut 2024-09-11T07:00:10.730539Z

I'll see if I can improve the doc later today...

miwal 2024-09-11T07:04:04.444559Z

So these are just 'equivalent' (if this is all you want - with no route destination; not realistic example)

[["/ping" ::ping]
 ["/pong" {:name ::pong}]]
but if you're actually using a :get as a destination for the route, and thereby need an options map, then you can't use ping? i didn't see that at all (and is that what should happen?) when i attempted to look at the source, it seemed that isn't the way it's intended? (expansions i think its called).

opqdonut 2024-09-11T07:05:21.704279Z

Yeah, those are equivalent. I think the docs should primarily talk about the map format and mention the ::ping as a shortcut later.

👍 1
opqdonut 2024-09-11T07:06:00.897839Z

I'm not quite following the rest of your message, too many negations for me

miwal 2024-09-11T07:07:57.604249Z

if I use an options map for other reasons, then i can't use the first form, of keyword directly in the vector? (to be expanded into the map as :name) ?

juhoteperi 2024-09-11T07:12:05.155279Z

No

opqdonut 2024-09-11T07:17:53.406529Z

yeah, you're right, you can only have a bare keyword or a map as the route argument, not both

miwal 2024-09-11T07:20:29.748879Z

trying to work out why i'm eccentric for wanting to do that!

opqdonut 2024-09-11T07:21:09.994569Z

heh

opqdonut 2024-09-11T07:21:45.310839Z

TBH I haven't seen anyone use name-based routing in production, so that's why I can't really tell you if you're alone in this

miwal 2024-09-11T07:24:17.531469Z

oh, that info helps!

miwal 2024-09-11T07:24:59.328229Z

i can proceed now though, thanks 🙂

opqdonut 2024-09-11T07:49:33.674909Z

no problem, and sorry for the docs 😁

opqdonut 2024-09-11T08:26:27.934229Z

comments? https://github.com/metosin/reitit/pull/699

👍 1
miwal 2024-09-11T08:53:27.176869Z

i also keep a log of my beginner experiences; qu's encountered and solutions; to convert into a little getting started guide from my own angle in due course.

miwal 2024-09-11T09:06:27.081699Z

IMO no one set of docs can cater to all needs (e.g. exhaustive reference vs beginner guide), just like no one piece of software can or should try to cover all possible needs equally. So it's about the decision on the target audience really 🙂

opqdonut 2024-09-11T10:34:16.242129Z

but also fixing the obvious omissions and mistakes, like in this case

miwal 2024-09-12T05:55:03.039159Z

I've noticed that the examples for reitit-ring i've seen all use anonymous function defs for the :handler within the options map (rather than referring to a function defined elsewhere). Would it be correct to observe that this makes those functions untestable? (I guess that means unit tests). (Background, also, related - I started out by using the thread first macro in which to state the route vector, followed by ring middleware in the traditional ring style. One drawback there is that if there are any typos/compile errors, since everything is within a macro, the error messages are somewhat... opaque). So some advantages potentially in named function defs. This said, I certainly appreciate the readability of anonymous functions inline next to the paths, - it does makes the whole app nicely readable top to bottom.

opqdonut 2024-09-12T06:06:48.043809Z

There are a couple of different ways to do it. Some people like always using a named handler function. I prefer a style with an anonymous function that does parameter-munging that then calls a service function with a nice API. That is, I'm trying to isolate the HTTP-specific stuff in the reitit definition.

👍 1
opqdonut 2024-09-12T06:07:44.309289Z

It's usually best to test the handler functions as part of the router. You can e.g. just call the ring app with mocked request maps and check outputs. That way you get to test the integration between your middleware, coercions and your handler function.

👍 1
opqdonut 2024-09-12T06:08:50.511859Z

The examples probably use anonymous functions just for compactness/readability.