babashka 2025-07-25

How can I disable babashka/http-clients default behavior to parse a body. I am trying to make a GET request to a url that returns a 302 with no body, I want to get the response headers to parse the Location header. But babashka fails with Cannot open <nil> as a Reader.

✅ 1

(http/get "" {:client (merge http/default-client-opts
                                                               {:follow-redirects :never})})
(unfortunately http://cljdoc.org doesn't handle the HEAD method

(require '[babashka.http-client :as http]
         '[babashka.http-client.interceptors :as interceptors])

#_(prn (map :name interceptors/default-interceptors))

(prn
 (http/get ""
           {:client (merge http/default-client-opts
                           {:follow-redirects :never})
            :interceptors (filter #(not= :babashka.http-client.interceptors/decode-body (:name %))
                                  interceptors/default-interceptors)}))

❤️ 1

If you think this is a bug, then we can make this more robust by checking for nil first

wow:

/private/tmp/kondo-performance/metabase/src/metabase/session/task/session_cleanup.clj
Inspected all files in 12,99 seconds.
Heap used: 28.159408569335938 MB

I localized all memoize usage to run! now, so all memory is freed after run!

in graalvm native-image things have become slower though. maybe I shouldn't use a cache at all in some cases. needs more testing, but this is promising

nice job! .. but wrong thread?

whoops sorry!

> If you think this is a bug, then we can make this more robust by checking for nil first My gut says this is a bug, because I don't think I should have to jump through hoops like that just to make an http request that returns no body (which is perfectly valid and normal).

yeah seems like it

isssue (+ optional PR) welcome

What's weird is that the response I printed in the above script, doesn't even have a :status etc

Yea I just tested it, also no :headers

just :request and :`body`

oh I get it, client shouldn't be a map

so it works like expected, the client should just be like this:

(http/get ""
           {:client (http/client (merge http/default-client-opts
                                        {:follow-redirects :never}))
            })

🙏 1

the error is confusing since client can also be a function where you do your own thing

oh interesting

I see where I went wrong. The client docstring says "To get the same behavior as the (implicit) default client, pass default-client-opts." and I then passed the twiddled client-opts to request, not the client fn.

yeah, an easy mistake to make, glad we found it

If you need JVM clojure from within babashka, is the best solution just to shell out and call the clj/`clojure` cli? Maybe using babashka.process? Context is, I have a babashka cli tool with a bunch of subcommands, they all run fine under bb but now I need to add a couple of commands that need jvm stuff (kicking off Lucene indexing jobs, lot of interop). It's fine if these are slow since they'll be slow in any case - and I'd love to keep using the tool under bb since I call the other commands more often and they're much faster under bb. Thanks for any tips!

@ryantate You can use babashka.tasks/clojure from bb.edn or babashka.deps/clojure (which is really the same function) in code files

🙌 1

e.g.:

(babashka.deps/clojure "-X:foo:bar" {:arg 1})

Ah thanks, I had a vague memory there was a facility for this, I could not remember what it was and spent some time looking throug the docs but couldn't track it down.

but shelling out is also a fine option. in bb.edn you can do that using (shell "clojure" ...) which is the same as babashka.process/shell

OK cool, i'll see if tasks can be a good fit first though that sounds good

Just to clarify: does that clojure function run in-process or does it shell out to deps.clj or to the (JVM) CLI?

it runs deps.clj in process

Thanks. I'd seen in CI where bb logged that Clojure tools wasn't in the expected place (so it downloads it) when I used clojure but not when I used shell "clojure" so I wanted to double-check.

yeah, indeed

Is there much reason to prefer one over the other? (in a CI context specifically)

there was a period of time in which shelling out to clojure was very OS-specific and sometimes error-prone, the deps.clj built-in thing was solving that problem. also deps.clj is there anyway for dependencies in bb.edn. but if you want to call out to a specific version of clojure on your system, that's fine too. no strong preference

perhaps the github action can be improved by downloading the tools jar as part of the github action installation process when bb is selected

I tend to keep cli: updated and currently setup bb: 'latest' so there wouldn't be much difference. I guess just a minor performance penalty for shelling out to clojure?

that would likely be an improvement wrt/ caching too

Aye, I added .deps.clj (I think) to my GHA caches.

👍 1

~/.deps.clj right

👍🏻 1

If you can run the tasks from a running clojure server with a repl running, you can send code into the jvm clojure repl from babashka. we have a https://github.com/metabase/metabase/blob/master/bb.edn#L173-L192 for this. -n optional:

$ bb -repl -n a.namespace '(do-stuff)'

help for it printed below if that helps.

Task Name: -repl
 Eval a string of clojure code in the backend dev server
Usages:
  -n, --namespace NAMESPACE  Namespace to use for the task
  -p, --port PORT            Port to use for the task, defaults to value in .nrepl-port

Examples:

 bb -repl '(+ 1 1)'
 - Eval a form in the backend dev server in the user namespace

 bb -repl --namespace metabase.app-db.connection @application-db-counter
 - Eval a form in the backend dev server in the 'metabase.app-db.connection' namespace

 bb -repl --port 59498 --namespace metabase.app-db.connection '(read-string "::hello")'
 - Evaluate a keyword inside a namespace

There's also a small lib here to do a similar thing: https://github.com/babashka/nrepl-client but not sure if that's the goal of the OP

🙌 1

I think I took the code snippet from the babashka book. I added stdout and stderr capture and some other goodies, for claude to use, before there was clojure-mcp 😅