Search code examples
postclojureclj-http

Post Request in Clojure with clj-http - body not accepted?


With my post request the API documentation for the CRM I wish to post too requires that I post a JSON file.

The JSON file is a multi-level file which is seen in clojure as a persistent array map.

My code to post is:

(def contacts (http/post "https://api.close.com/api/v1/data/search" 
           {:basic-auth [api ""]
            :body closeFilter 
            })) 

CloseFilter represents the mutli-level JSON I wish to post.

However, I get the following error:

class clojure.lang.PersistentArrayMap cannot be cast to class [B (clojure.lang.PersistentArrayMap is in unnamed module of loader 'app'; [B is in module java.base of loader 'bootstrap')

What mistake am I making here?

Update

I am recreating a program I have in Javascript. Posting the same file works perfectly.

Update 2 - MRE

I am still struggling with this so here is an example of my code.

My code starts off by requiring the packages I need:

(ns schedule-emails.core
  (:require [clj-http.client :as http]
            [clojure.data.json :as json]
            [cheshire.core :refer :all]))

Then, I parse a local JSON file from my file system into the app. The JSON. This returns a map of maps with embedded vectors.

(def closeFilter
  (json/read-str
   (slurp "URL TO LOCAL FILE")))

Finally, I want to post this information from the local file to the software:

def contacts (http/post "API URL HERE"
           {:accept :json
            :as :json
            :content-type :json
            :basic-auth [api ""]
            :body closeFilter}))

However, I get the following error:

class clojure.lang.PersistentArrayMap cannot be cast to class [B (clojure.lang.PersistentArrayMap is in unnamed module of loader 'app'; [B is in module java.base of loader 'bootstrap')

I also tried the suggested solution below but i'm getting the same issue.


Solution

  • clj-http does not on it's own negotiate with some backend what it expects and coerce the transferred data "automatically". You can however configure, that in the case of JSON, so some data with the proper content-type will be transformed both from the body into the request, and back to data from the response using JSON.

    1. So you usually want the following things in the request:

      {:as :auto
       :coerce :always
       :content-type :application/json
       :body ...
       ; your own additional stuff...
       }
      

      So add a mime-type, so both clj-http knows what to do and the backend knows what it gets.

      See input coercion and output coercion

    2. and you have to make sure, that the means to make it actually working are there. This means, that you have added cheshire as dependency. See optional dependencies

    Another option of course is do deal with this yourself. So you would have add a library, that can create JSON from/to a string or stream, the the content-type and transform the body/response.