This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-01-18
Channels
- # adventofcode (69)
- # babashka (21)
- # beginners (246)
- # calva (49)
- # chlorine-clover (19)
- # circleci (3)
- # clj-kondo (38)
- # cljsrn (1)
- # clojure (52)
- # clojure-australia (2)
- # clojure-europe (41)
- # clojure-nl (5)
- # clojure-spec (4)
- # clojure-taiwan (2)
- # clojure-uk (28)
- # clojurescript (12)
- # cryogen (6)
- # cursive (6)
- # datahike (3)
- # deps-new (5)
- # fulcro (2)
- # garden (1)
- # graalvm (3)
- # hoplon (48)
- # jackdaw (6)
- # jobs (3)
- # kaocha (6)
- # malli (3)
- # off-topic (51)
- # rdf (1)
- # reagent (40)
- # reitit (32)
- # remote-jobs (1)
- # reveal (24)
- # shadow-cljs (21)
- # startup-in-a-month (5)
- # xtdb (8)
Do I have to re-eval the router definition every time I change my handler?
(def app
(ring/ring-handler
(ring/router
[["/" main-page]
["/config" {:post config/save}]]) ; config/save cannot be used as var here and thus you need to re-eval `app` every time
...
@jumar it doesn’t work with Var? It could, and there is a multimethod to handle how things are expanded, Var could be added as default mapping to it.
With var I get this error:
No implementation of method: :expand of protocol: #'reitit.core/Expand found for class: clojure.lang.Var
(extend-protocol reitit.core/Expand
clojure.lang.Var
(expand [this _] {:handler this}))
Yes! You are super-helpful, thanks! Is it worth filing an issue or should I just keep it in my code?
you could also re-create the top-level ring handler for all requests in dev. quite slow, but always up-to-date, e.g. (defn app [request] ((ring/…) request))
. Personally using integrant-repl and have keyboard mapping of shift + Cmd + ö
to run reset. takes few millis so, just that.
I try to avoid using tools.namespace refresh (if that's the thing integrant uses) - at least when changes are small.
Reloading the app (I do that now) is easy when it's in the same namespace
but since config/save
is another ns it's more annoying 🙂.
I know it might not be the best place to ask this, but I am trying to run the example from Reitit+Malli, and I can create a post request with httpie, but I failed to make the same request with http-kit
Did anyone go through the same trouble?
(ns test.server
(:require
[org.httpkit.client :as client]
[cheshire.core :as j]))
;; you can use babashka for this example
(->>
(client/post
""
{:accept :json
:body (j/encode {:x 1 :y 20}) :as :text})
deref
:body
println)
;; => {"schema":"[:map [:x any?] [:y any?]]","errors":[{"in":[],"value":null,"message":"invalid type","schema":"[:map [:x any?] [:y any?]]","path":[],"type":"malli.core/invalid-type"}],"value":null,"type":"reitit.coercion/request-coercion","coercion":"malli","in":["request","body-params"],"humanized":["invalid type"]}
The funny thing is this works fine
http POST :3000/math/plus x:=1 y:=20
The content type isn't being set on the http-kit client, whereas in httpie
, it's set automatically as application/json
Let me double check
nope, it it is not that 😕
It drives me crazy haha
I am fairly certain it has to play with the coercion, because when I just make an echo of the request, I do see a json body.
(ns ch.my.server
(:require
;; Uncomment to use
;; [reitit.http.interceptors.dev :as dev]
;; [spec-tools.spell :as spell]
[reitit.swagger :as swagger]
[clojure.core.async :as a]
[ :as io]
[clojure.pprint :as pprint]
[integrant.core :as ig]
[io.pedestal.http :as server]
[muuntaja.core :as m]
[muuntaja.interceptor]
[malli.util :as mu]
[reitit.coercion.malli]
[reitit.dev.pretty :as pretty]
[reitit.http :as http]
[reitit.http.coercion :as coercion]
[reitit.http.interceptors.exception :as exception]
[reitit.http.interceptors.multipart :as multipart]
[reitit.http.interceptors.muuntaja :as muuntaja]
[reitit.http.interceptors.parameters :as parameters]
[reitit.pedestal :as pedestal]
[reitit.ring :as ring]
[reitit.ring.malli]
[reitit.swagger-ui :as swagger-ui]))
(defn interceptor [x]
{:enter (fn [ctx] (update-in ctx [:request :via] (fnil conj []) {:enter x}))
:leave (fn [ctx] (update-in ctx [:response :body] conj {:leave x}))})
(def routes
[["/echo"
{:get {:handler
(fn [context] (pprint/pprint context)
{:status 200
:body (slurp (:body context))})}
:post {:handler
(fn [context] (pprint/pprint context)
{:status 200
:body (slurp (:body context))})}}]
["/swagger.json"
{:get {:no-doc true
:swagger
{:info {:title "my-api"
:description "with [malli]() and reitit-ring"}
:tags [{:name "files", :description "file api"}
{:name "math", :description "math api"}]}
:handler (swagger/create-swagger-handler)}}]
["/math"
{:swagger {:tags ["math"]}
#_#_:interceptors [(interceptor :api)]}
["/plus"
{:get {:summary "plus with malli query parameters"
:parameters {:query [:map [:x int?] [:y int?]]}
:responses {200 {:body [:map [:total int?]]}}
:handler (fn [{{{:keys [x y]} :query} :parameters}]
{:status 200
:body {:total (+ x y)}})}
:post {:summary "plus with malli body parameters"
:parameters {:body [:map [:x int?] [:y int?]]}
;; :responses {200 {:body [:map [:total int?]]}}
:handler (fn [{{{:keys [x y]} :body} :parameters :as context}]
(pprint/pprint context)
{:status 200
:body {:total (+ x y)}})}}]]])
(def router-args
{;:reitit.interceptor/transform dev/print-context-diffs ;; pretty context diffs
;;:validate spec/validate ;; enable spec validation for route data
;;:reitit.spec/wrap spell/closed ;; strict top-level validation
:exception pretty/exception
:data
{:coercion (reitit.coercion.malli/create {:compile mu/open-schema})
:muuntaja m/instance
:interceptors
[;; swagger feature
;; swagger/swagger-feature
;; ;; format
(muuntaja/format-interceptor)
;; query-params & form-params
(parameters/parameters-interceptor)
;; content-negotiation
(muuntaja/format-negotiate-interceptor)
;; decoding request body
(muuntaja/format-request-interceptor)
;; encoding response body
(muuntaja/format-response-interceptor)
;; exception handling
(exception/exception-interceptor)
;; coercing response bodys
(coercion/coerce-response-interceptor)
;; coercing request parameters
(coercion/coerce-request-interceptor)
;; multipart
#_(multipart/multipart-interceptor)]}})
(defn router []
(pedestal/routing-interceptor
(http/router routes router-args)
;; optional default ring handler (if no routes have matched)
(ring/routes
(swagger-ui/create-swagger-ui-handler
{:path "/"
:config {:validatorUrl nil
:operationsSorter "alpha"}})
(ring/create-resource-handler)
(ring/create-default-handler))))
(defn start [{:keys [port router]}]
(let [service-map (-> {:env :dev
::server/type :jetty
::server/port (or port 3000)
::server/join? false
;; no pedestal routes
::server/routes []
;; allow serving the swagger-ui styles & scripts from self
::server/secure-headers
{:content-security-policy-settings
{:default-src "'self'"
:style-src "'self' 'unsafe-inline'"
:script-src "'self' 'unsafe-inline'"}}}
(server/default-interceptors)
;; use the reitit router
(pedestal/replace-last-interceptor router)
(server/dev-interceptors)
(server/create-server))]
(server/start service-map)
service-map))
(def system-config
{::jetty {:port 3000
:join? false
:router (ig/ref ::router)}
::router {}})
(defmethod ig/init-key ::jetty [_ {:keys [port join? router]}]
(println "server running in port" port)
(start {:port port :join? join? :router router}))
(defmethod ig/halt-key! ::jetty [_ service-map]
(println "Stopping" service-map)
(server/stop service-map))
(defmethod ig/init-key ::router [_ _]
(router))
(defn -main []
(ig/init system-config))
(client/post
""
{:headers {"Content-Type" "application/json"}
:body (j/encode {:x 1 :y 20})})
❯ cat foo.clj
(ns foo
(:require
[org.httpkit.client :as client]
[cheshire.core :as j]))
;; you can use babashka for this example
(->>
(client/post
""
{:headers {"Content-Type" "application/json"}
:body (j/encode {:x 1 :y 20})})
deref
:body
println)
~
❯ bb foo.clj
{"total":21}
Thanks!
I am sorry for the trouble...
@gupta.harsh96 Small world. 🙂 If you're evaluating reitit, fwiw, I fully endorse.
Thanks! reitit, is super cool 😄