Fork me on GitHub
#reitit2017-11-21
>
dotemacs02:11:22

o/ Thanks for writing reitit, I like it! I might come and bug you now with questions about coercion and like. You have been warned! 🙂

ikitommi07:11:59

Glad you like it! Question are good, we'll try to make awesome docs with a FAQ. Need the questions first ;)

ikitommi08:11:02

Quick status update for 0.1.0: * replace prefix-tree-router with segment-router (correctness & faster) * reitit-schema (the schema coercion module) * more tests Next steps: * extra stuff for the data-driven middleware / interceptors (written as issues) * reitit-cljs? (extract from the ongoing projects, with the lovely routing controllers) * reitit-openapi (swagger-stuff, should be trivial) * reitit-interceptor (all the mw stuff ported to interceptor-model)

ikitommi08:11:38

the routing is already quite fast, but with the new segment-router, it’s 30% faster. Which is cool imo, if not useful 🙂

yogthos13:11:58

as I mentioned in the reddit thread, it seems like you could just add the additional keys compojure-api style

["/api" {:middleware [[wrap :api]]}
 ["/subtract" {:get handler
            :query-params [x :- Long, {y :- Long 1}]
            :return       Long
            :name         ::subtruct}]

yogthos13:11:03

the nice part about this is that you could start with plain reitit and then add reitit-api on top of that

yogthos13:11:48

to generate the swagger API

ikitommi14:11:42

Currently, the coercion module uses the ring-swagger/openapi format for the routes. So, you can write the example as:

(require '[reitit.ring :as ring])
(require '[reitit.ring.coercion :as coercion])
(require '[reitit.ring.coercion.schema :as schema])

(def app
  (ring/ring-handler
    (ring/router
      ["/api" {:middleware [[wrap :api]]}
       ["/subtract" {:get handler
                     :parameters {:query {:x Long, :y Long}}
                     :responses {200 {:schema Long}}
                     :name ::subtruct}]]
      {:data {:middleware [coercion/gen-wrap-coerce-parameters
                           coercion/gen-wrap-coerce-response]
              :coercion schema/coercion}})))

ikitommi14:11:39

to support :query-params [x :- Long, {y :- Long 1}] format - the whole endpoint would need to be wrapped into a unevaluated form. As the y and x would fail otherwise.

ikitommi14:11:22

To support the fnk notation, we could extract the schema form the handler. So this would work:

ikitommi14:11:48

(require '[reitit.ring :as ring])
(require '[reitit.ring.coercion :as coercion])
(require '[reitit.ring.coercion.schema :as schema])
(require '[plumbing.core :refer [fnk]])

(def app
  (ring/ring-handler
    (ring/router
      ["/api" {:middleware [[wrap :api]]}
       ["/subtract" {:get (fnk [[:parameters [:query x :- Long, y :- Long]]]
                            {:status 200, :body (+ x y)})
                     :responses {200 {:schema Long}}
                     :name ::subtruct}]]
      {:data {:middleware [coercion/gen-wrap-coerce-parameters
                           coercion/gen-wrap-coerce-response]
              :coercion schema/coercion}})))

ikitommi14:11:45

that would keep the routing tree as just data.

ikitommi14:11:17

what do you think @yogthos?

yogthos14:11:39

yeah using just data is better

yogthos14:11:08

this is just as readable as schema syntax in my opinion:

:parameters {:query {:x Long, :y Long}}
:responses {200 {:schema Long}

ikitommi14:11:11

I’m planning on using spec to make the route data syntax sane: different parts of the system (router, middleware, interceptors) can tell a spec about the route data requirements they have. The partial specs will be merged to get the overall route data spec. All route data is validated against this at router creation time -> will notify the user if there are typos in the keys, like figwheel does.

ikitommi14:11:58

If you have a key :auth in a route, which doesn’t have a authorize-middleware (telling it’s interested in the :auth key), user will get a warning of unhandled key.

ikitommi14:11:12

good thing is that we know the whole mw/interceptor chain for each route, and the mws are just data, so we can optimize things.

yogthos14:11:59

yeah that's handy

ikitommi14:11:02

e.g. apply parse-request-body for the whole router, but unmount it for all routes that are not interested in the :body parameters.

yogthos14:11:19

and it looks like applying middleware selectively to specific branches becomes much cleaner than when using compojure

yogthos14:11:58

so yeah I definitely want to move luminus over at some point

yogthos14:11:30

most work will be in rewriting docs and examples 🙂

ikitommi16:11:59

To get real benefits of data-driven middleware, middleware should be wrapped into Middleware records.

ikitommi16:11:04

at least, all mw should have a unique :name. There will be an Inventory api in reitit: to list all things registered: schemas, specs, mw, endpoints etc.

ikitommi16:11:11

Should there be a community repo of all the good mws repackaged? (or rewritten)

ikitommi16:11:21

But, it would be lovely to need a CORS Mw, and have in packaged so that when asking from an endpoint about its mounted mw, it would say [#Middleware{:name ::cors, :description "adding cors"}] instead of [#object[a.b.c 0x79e89cd2 “a.b.c$cors-middlware@79e89cd2”]

ikitommi16:11:36

Also, IMO all mw should describe formally what they require & do. Allows automatic re-ordering, un-mounting etc.

yogthos19:11:34

yeah I really think that middleware needs to work a lot like dependencies do in lein

yogthos19:11:35

composing middleware by hand is very error prone, and it makes it difficult to write libraries that compose middleware as they can easily conflict with each other