reitit icon indicating copy to clipboard operation
reitit copied to clipboard

response-coercer customisation

Open lsnape opened this issue 4 years ago • 0 comments

The spec/spec-tools implementation of the Coercion protocol calls st/coerce to transform both requests and responses – the -response-coercer is essentially the same as -request-coercer, just called with a different type argument used to look up :response transformers. This makes it impossible to have request and response specs share child specs, as well as having the request coercer decode the incoming value, and the response coerce encode the outgoing value.

Take as an example:

(s/def ::amount 
  (st/spec :spec amount?
           :encode/json str
           :decode/json bigdec))

(s/def ::request-body (s/keys :req-un [::amount])
(s/def ::response-body (s/keys :req-un [::amount])

I would like decode/json to be applied by the request coercer, and encode/json by the response coercer, but this won't work with the above example, nor any request / response specs with shared children.

I find sharing specs in this way is useful because:

  • re-use: child specs often appear in request and response bodies;
  • validation happens after decoding, and before encoding – in other words, what the application code 'sees';
  • and encoder and decoder functions are defined in one place.

I have explored the behaviour and attempted a workaround but to no avail. The response-coercer default transformer is in fact a no-op, but overriding it with json-transformer will invoke decode/json as a result of calling st/coerce, not encode/json as I would like. It's possible to define a custom JSON transformer with encode and decode functions switched, but this blows up when the result is validated against the spec.

I know that changing the default behaviour would be a breaking change and therefore out of the question, but it would be useful to have reitit.coercion.spec/create accept an option that, when provided, encodes the outgoing value in the response coercer. If we did that then the validation cannot happen after the encoding like it does currently; we will instead have to introduce a new conditional before the call to st/coerce to handle this new behaviour.

If a solution is agreed then I'm more than happy to submit a PR for this.

lsnape avatar Dec 27 '20 20:12 lsnape