0

compojure-api allows you to define the return schema:

(s/defschema Pizza
  {:name s/Str})

(GET "/pizza/:id" [id]
  :return Pizza
  :summary "returns a Pizza"
  (ok (get-pizza id)))

My issue is when the get-pizza fn returns a 404 {:status 404 :body {:message "Invalid id provided"}} the response (to the requester) is that that 404 does not match the Pizza schema:

{
  "errors": {
    "name": "missing-required-key",
    "status": "disallowed-key",
    "body": "disallowed-key"
  }
}

The docs show that you can provide multiple response schemas but I am unclear on what my get-pizza fn should return in order to utilize those schemas. Their example uses the ring-http-response lib to generate the return values, but there is no difference between my return value for get-pizza and the ring-http-response 'not found' fn.

Truthfully I would rather not use the :responses param if possible because I can already foresee all the code duplication. Before specifying the :return Schema I was able to have my get-pizza fn return a 404 and be correctly passed onto the requester but once I added the Schema I was no longer able to do that. I considered having the return value be :return (s/one Pizza Error) where Error would be a generically defined error map but I don't see why I would do this for every route if all calls could in theory return a 500.

Note: The reason I switched to utilizing the :return param is that it makes the generated swagger-ui docs much prettier & easier to understand.

kittyminky
  • 478
  • 6
  • 27

1 Answers1

-1

You have a small issue in your code I assume as you didn't show implementation of your get-pizza [id] function.

You return {:status 404 :body {:message "Invalid ID provided}} which is then wrapped into HTTP 200 response by (ok (get-pizza id)).

Your get-pizza function should return a pizza or nil if not found. Your HTTP response should be generated based on that in your route:

(GET "/:id" []
  :path-params [id :- Long]
  :return Pizza
  :summary "Gets a pizza"
  (if-let [pizza (get-pizza id)]
    (ok pizza)
    (not-found {:message "Invalid ID provided"})))

The original example from compojure-api repo has a bit different implementation where for nil values from get-pizza it will still return HTTP 200 response with empty response body. According to your question you would return 404 instead and the code above should meet your requirement.

Piotrek Bzdyl
  • 12,965
  • 1
  • 31
  • 49
  • The code above returns the same error because the response of `not-found` does not match the defined `:return` value of the `Pizza` schema. It does work when I do `(s/maybe Pizza)` but it seems unreasonable to have to wrap *all* of my return values in `s/Maybe` when not all of them will return 200s. `get-pizza` lives in another file and i want the `get-pizza` fn to be able to return a 404 that this path definition will then properly process and was wondering if there was a way with compojure-api to do *and* still have a defined return fn since it doesn't seem to be an unreasonable use case. – kittyminky Feb 25 '16 at 20:53
  • Did you change the route definition to use `not-found` when `nil` pizza is returned? You can try with a modified example from my fork: `git clone https://github.com/pbzdyl/compojure-api` `cd compojure-api` `git checkout stackoverflow-35618493` `lein run` Then in another terminal: `curl -v http://localhost:3000/api/pizzas/1000` and you will get HTTP 404 and the message. – Piotrek Bzdyl Feb 25 '16 at 21:02
  • There was an error in your code that didn't let it run but using not-found works. My question though is that I want a way to just have `(get-pizza id)` return the response i want rendered (status code and all). Otherwise for every single route I will have to separately check for 200,404,409 etc – kittyminky Mar 01 '16 at 17:53
  • Could you provide more info what error you were getting? Regarding your concern to have your `get-pizza` to render the whole response - this way you are moving the same problem deeper into your code. Now your handler generates the response using `ok` or `not-found` and you want to generate responses manually as maps in `get-pizza` function. – Piotrek Bzdyl Mar 01 '16 at 17:59
  • It was a local dependency issue, it runs now! And I found an example in their wiki that has a similar approach except it has a separate fn wrapping that `get-pizza`, this is the approach I will go with since for most of the routes I have to handle more than just 200s & 404s https://github.com/JarrodCTaylor/authenticated-compojure-api/blob/master/src/authenticated_compojure_api/route_functions/user/create_user.clj#L17 – kittyminky Mar 01 '16 at 19:09