Hello! I'm new to reitit, but I'm trying to build an application in Clojure with a reitit API. I'm trying to set up path parameter validation for a get route. The route has the form of resource/:uuid, and I basically just want to ensure that the path parameter is a uuid and if not return a 400 with an appropriate error code and message to the client. I think what I have so far is actually validating the path parameter, but a 500 is being thrown, so I'm not 100% sure about that. Mostly, I would love some guidance in getting pointed in the right direction with this. If there are any great resources that talk about this, I would appreciate the recommendations.
the exception-middleware can take options on how to handle specific error types
https://github.com/metosin/reitit/blob/master/doc/ring/exceptions.md
:reitit.coercion/request-coercion
Can on the malli side you can control those property messages with :error/message or :error/fn: https://github.com/metosin/malli/?tab=readme-ov-file#custom-error-messages
We are using the Malli library with reitit and we validate that a path parameter is a uuid as follows
["/document/:id"
{:get {:summary "Get document by id"
:responses {200 {:content {:default {:schema #'schema/document}}}}
:parameters {:path [:map [:id uuid?]]}
:handler (fn [{:keys [path-params authorized-person]}]
{:status 200
:body (document/get-by-id db/node
(parse-uuid (:id path-params))
(:id authorized-person))})}}]:uuid and uuid? predicate are about the same thing (but not exactly)
That is true, missed that :uuid definition in the second example 👀
Doesn't :uuid schema use uuid? fn under the hood as a predicate? What is the "not exaclty" difference here?
(some) schema properties only with with "simple-schemas" (i.e. the named version, not predicates)
And due to impl details with predicates (they are being stored as vars into the schema registry and then need to be checked for equality or something...) there are some special environments where predicate schemas don't work: https://github.com/metosin/malli/issues/556
for example :string length :min :max properties are defined for :string schema
From readme: NOTE: Predicate Schemas do not cover any schema properties, e.g. string? can't be modified with properties like :min and :max. If you want to use the schema properties, use real schema types instead, e.g. :string over string?.
Ah, makes sense, thanks!
Here is the snippet with the router definition:
(defn- route->handler
[app]
{:health {:get health}
:get-document {:handler (partial docs/get-document (:documents-repository app))
:parameters {:path DocumentId}}})
(defn- router
[route-map]
(ring/ring-handler
(ring/router
[base-api-url
["/v1"
["/health" (:health route-map)]
["/documents/:id" (:get-document route-map)]]]
{:data {:coercion rcm/coercion
:muuntaja (m/create
(assoc-in
m/default-options
[:formats "application/json" :encoder-opts]
{:encode-key-fn (comp csk/->camelCase name) :strip-nils true}))
:middleware [muuntaja/format-middleware
coercion/coerce-request-middleware]}})))Okay, I'm making progress. Here is what I have now:
(defn- route->handler
[app]
{:health {:get health}
:search-documents {:get (fn [_] {:status 200 :body "Searching documents"})}
:get-document {:get (partial docs/get-document (:documents-repository app))
:parameters {:path {:id :uuid}}}})
(defn- router
[route-map]
(ring/ring-handler
(ring/router
[base-api-url
["/v1"
["/health" (:health route-map)]
["/search" ["/documents" (:search-documents route-map)]]
["/documents/:id" (:get-document route-map)]]]
{:data {:coercion rcm/coercion
:muuntaja (m/create
(assoc-in
m/default-options
[:formats "application/json" :encoder-opts]
{:encode-key-fn (comp csk/->camelCase name) :strip-nils true}))
:middleware [muuntaja/format-middleware
coercion/coerce-exceptions-middleware
coercion/coerce-request-middleware]}})))
which does give me a 400 error with the response
{
"value": {
"id": "not-a-uuid"
},
"type": "reitit.coercion/request-coercion",
"coercion": "malli",
"in": [
"request",
"path-params"
],
"humanized": {
"id": [
"should be a uuid"
]
}
}I guess if I wanted to transform that error response into something else, I would either need to write my own coercion middleware, or write another middleware to take the response and transform it into the desired output.