Compojure: How to get query parameters as a map - clojure

I just created my RESTful service through Luminus using this doc: http://www.luminusweb.net/docs/services.md
Apparently Compojure-API uses Schema library to map the query parameters. However, I would like to be able to get all the query parameters as a single map instead. For instance:
From this GET /api/myapp?color=green&shape=round&height=100
to this {:color "green", :shape "round", :height "100"}
thanks!

Got it.
I used this as an example (GET "/test" {params :params} (str params)). params is a map with keys and values for the given query params.

Related

Clojure http-kit: get query params as map?

I have a server with an endpoint .../end2 which I send parameters to, such as:
.../end2?a=2&b=1
How do I get a map {:a 2 :b 1}? I thought (:params request) is the way to go but I get an empty map..
Assuming you're using compojure, the params are not automatically bound to the request, and ring middleware must be applied to do this:
(defroutes app-routes
(GET "/end2" request (str (:params request))))
(def app
(-> app-routes
ring.middleware.params/wrap-params))
(run-server #'app {:port 8888})
You need to add ring middle ware to parse params. You could check ring default
You don't have to worry about nested params or others.

How to get the URL query params in Pedestal?

How do I get the URL params into the request map in Pedestal? I am assuming that this needs the use of an interceptor? However the Pedestal documentation (or severe lack thereof) does not make this at all clear. Thanks.
Query parameters are parsed automatically by Pedestal, and the resulting map is placed in the request map under the :query-params key.
As a simple example, start with the pedestal-service template and use the following definitions:
(defn home-page
[request]
(ring-resp/response (format "Hello with params: %s" (:query-params request))))
(defroutes routes
[[["/" {:get home-page}]]])
Now if you browse to http://localhost:8080/?param=true&other=1234, you should see Hello world with paramters: {:param "true", :other "1234"}.

Getting a clojure map with keywords from a POST using Liberator

I'm using Liberator, and am having a hard time getting my POSTed data into a map using with keywords as the keys. Here is my resource, with a few printlines for testing:
(defresource finish_validation
:allowed-methods [:post]
:available-media-types ["application/json"]
:post! (fn [context]
(let [params (slurp (get-in context [:request :body]))
mapped_params (cheshire/parse-string params)]
(println (type params))
(println (type mapped_params))
(validation/finish mapped_params)))
:handle-created (println ))
For testing, I'm posting the data using curl:
curl -H "Content-Type: application/json" -X POST -d '{"email":"test#foo.com","code":"xyz"}' http://localhost:8080/validate
cheshire converts the params into a map, but the keys are not keywords: I get {email test#foo.com, code xyz} as the output, instead of the hoped-for {:email test#foo.com, :code xyz}.
Should I be doing something differently? Is this even the right approach to getting the data?
You need to leverage ring's wrap-params middleware, coupled with the wrap-keyword-params middleware which converts the params map to a key map.
(ns your.namespace
(:require [ring.middleware.params :refer [wrap-params]]
[ring.middleware.keyword-params :refer [wrap-keyword-params]]))
(def app
(-> some-other-middleware
wrap-keyword-params
wrap-params))
Using this middleware with wrap-params converts params to use keys. After adding this middleware, you can access your params from the request map, like so (-> ctx :request :params). No need to convert them per request. This will handle all requests.
I just had to put "true" at the end of the call to the cheshire function, and the keys are returned as keywords:
(cheshire/parse-string params true)
Depending on your requirements, you can simplify the handling of your post data using various ring middleware. This will allow you to process your json data in one place and eliminate the need to have duplicate data processing in each of your handlers/resource definitions. There are a few ways of doing this. You can have the json data added as keywordized parameters in the params map or a json-params map. Have a look at ring.middleware.format and ring.middleware.json.

How to convert korma select results to json for a rest service (compojure)?

I am using compojure, cheshire and korma (and postgre db) for creating a rest service.
I've created a table with two string fields (name and description) with such structure:
(defentity posts
(pk :id)
(table :posts)
(entity-fields :name :description))
I can insert records into this table but when I try to exec
(defn get-all-posts []
(select posts))
and return the results from the server
defroutes app-routes
(GET "/" [] (get-start))
(context "/posts" []
(GET "/" [] (get-all-posts))
...
I receive such an error:
java.lang.IllegalArgumentException
No implementation of method: :render of protocol: #'compojure.response/Renderable found for class: clojure.lang.PersistentVector
As I see I need to convert posts collection to json. How to do it?
Ring responses can be either a map or a string. If they are a map then they use a few keys such as :status and :body to define the response and set cookies etc. You may want to explicitly convert your response from a Clojure sequence (edn) to JSON by wrapping the call to (get-all-posts) in generate-string (since you are using Cheshire) :
{:status 200
:content-type "application/json; charset=UTF-8"
:body (cheshire/generate-string (get-all-posts))}
And while you are at it it can't hurt to specify the content type and response code.

Compojure: optional URL parameter

I want to define a resource in Compojure like this:
(ANY "myres/:id" [id] (handler))
and I want the :id to be optional (depending on whether or not the ID is specified my API will behave differently).
This works ok if I try to access
http://mydomain/myres/12
However if I try to access
http://mydomain/myres
without specifying an ID, I get 404.
Is there any way to have the parameter :id to be optional?
Thanks!
What about creating 2 different route one with id and another without it and calling your handler from both route as shown below:
(defn handler
([] "Response without id")
([id] (str "Response with id - " id)))
(defroutes my-routes
(ANY "myres" [] (handler))
(ANY "myres/:id" [id] (handler id)))