babashka.http-client

repository·main·Indexed 40 days ago

https://github.com/babashka/http-client

A scripting-friendly HTTP client for Clojure and Babashka built on java.net.http. It provides an API compatible with babashka.curl and is built into Babashka as of version 1.1.171. Features include support for GET, POST, PUT, PATCH, and DELETE requests, asynchronous execution via CompletableFuture, multipart requests, interceptors, and custom client configuration for SSL, proxies, and authentication.

Tokens
5.5K
Snippets
26
Records
31
Agent score
50%

What's inside babashka.http-client

  1. Overview of http-client

    main

    http-client is an HTTP client for Clojure and Babashka built on top of java.net.http.

    It is designed to be a good default for most scripting use cases and can be used as a dependency-free JVM library. The API is designed to be mostly compatible with babashka.curl, allowing it to serve as a drop-in replacement for many workflows.

  2. How interceptors work

    main

    Interceptors are maps containing a :name (string), a :request function, and a :response function. They allow you to modify requests before they are sent and responses before they are returned to the user.

    • :request function: Executed when the request is built.
    • :response function: Executed when the response is received.

    Interceptors can be passed per-request via the :interceptors key. The default interceptors are available in babashka.http-client.interceptors/default-interceptors.

    ;; Example of a custom JSON interceptor
    {:name ::json
     :request (fn [request]
                (if (= :json (:as request))
                  (-> (assoc-in request [:headers :accept] "application/json")
                      (assoc :as :string ::json true))
                  request))
     :response (fn [response]
                  (if (get-in response [:request ::json])
                    (update response :body #(json/parse-string % true))
                    response))}
  3. Use interceptors to modify requests and responses

    main

    Interceptors are functions used to transform requests and responses. The default-interceptors chain is used by default. Interceptors are applied in order for requests and in reverse order for responses.

    Common interceptors in babashka.http-client.interceptors include:

    • accept-header: Adds :accept header (only supports :json).
    • basic-auth: Adds :authorization header based on :basic-auth in the request.
    • construct-uri: Constructs a URI from a map.
    • decode-body: Decodes the response body based on the :as option (:string, :stream, or :bytes; defaults to :string).
    • decompress-body: Decompresses the body using gzip or deflate based on the content-encoding header.
    • form-params: Encodes :form-params map and adds it to the :body.
    • multipart: Handles multipart request bodies and headers.
    • oauth-token: Adds :authorization header based on :oauth-token string.
    • query-params: Encodes :query-params map and appends to :uri.
    • request-method: Normalizes the :method option.
    • throw-on-exceptional-status-code: Throws on exceptional status codes.
    • unexceptional-statuses: (Internal/Utility for status handling).
  4. Install http-client

    main

    You can install http-client as a dependency in your deps.edn or bb.edn file.

    Note that babashka.http-client is built into Babashka as of version 1.1.171, so you may not need to add it manually if you are using a recent version of Babashka.

    org.babashka/http-client {:mvn/version "0.4.22"}
  5. Debug HTTP requests via JVM system properties

    main

    To debug HTTP requests, you must add a JVM system property to enable logging in the underlying Java HTTP client.

    Add the following to your deps.edn aliases:

    :aliases {
     :debug
     {:jvm-opts
      ["-Djdk.httpclient.HttpClient.log=errors,requests,headers,frames[:control:data:window:all..],content,ssl,trace,channel"]}
    }
  6. Configure GET query parameters

    main

    Use the :query-params key to pass parameters. To send multiple values for the same key, provide a vector of values.

    ;; Single parameter
    (http/get "https://postman-echo.com/get" {:query-params {"q" "clojure"}})
    
    ;; Multiple parameters for the same key
    (http/get "https://postman-echo.com/get" {:query-params {:q ["clojure" "curl"]}})
  7. Construct URIs with the verbose :uri API

    main

    For safer and more fine-grained URI construction, pass a map to the :uri key in http/request instead of a string.

    (http/request {:uri {:scheme "https"
                          :host   "httpbin.org"
                          :port   443
                          :path   "/get"
                          :query  "q=test"}})
  8. Handle HTTP exceptions and status codes

    main

    By default, an ExceptionInfo is thrown for status codes other than: #{200 201 202 203 204 205 206 207 300 301 302 303 304 307}.

    To prevent exceptions from being thrown, set :throw false in the request options.

    ;; This will throw an ExceptionInfo
    (http/get "https://httpbin.org/status/404")
    
    ;; This will return the response map with status 404 instead of throwing
    (http/get "https://httpbin.org/status/404" {:throw false})
  9. Perform GET requests

    main

    Use http/get to perform a simple GET request. The returned response map contains :status, :body, and :headers.

    (require '[babashka.http-client :as http])
    
    (http/get "https://httpbin.org/get")
    ;;=> {:status 200, :body "{ ... }", :headers { ... }}
  10. Perform POST requests

    main

    Use http/post to send data. You can send a string body, a JSON-encoded body, a file, an input stream, or form parameters.

    (require '[babashka.http-client :as http]
           '[clojure.java.io :as io]
           '[cheshire.core :as json])
    
    ;; String body
    (http/post "https://postman-echo.com/post" {:body "From Clojure"})
    
    ;; JSON body
    (http/post "https://postman-echo.com/post"
               {:headers {:content-type "application/json"}
                :body (json/encode {:a 1 :b "2"})})
    
    ;; File body
    (http/post "https://postman-echo.com/post" {:body (io/file "README.md")})
    
    ;; Stream body
    (http/post "https://postman-echo.com/post" {:body (io/input-stream "README.md")})
    
    ;; Form parameters
    (http/post "https://postman-echo.com/post" {:form-params {"name" "Michiel"}})
  11. Use Basic Auth and OAuth tokens

    main

    Authenticate requests using :basic-auth (passing a [username password] vector) or :oauth-token (passing a string).

    ;; Basic Auth
    (http/get "https://postman-echo.com/basic-auth" {:basic-auth ["postman" "password"]})
    
    ;; OAuth Token
    (http/get "https://httpbin.org/bearer" {:oauth-token "qwertyuiop"})
  12. Manage WebSocket messages and lifecycle

    main

    Once a WebSocket connection (ws) is established, use the following functions to interact with it:

    • (send! ws data opts): Sends a message. data can be a CharSequence (string), byte array, or ByteBuffer. Use :last true (default) to indicate the final message.
    • (ping! ws data): Sends a Ping message with the provided bytes.
    • (pong! ws data): Sends a Pong message with the provided bytes.
    • (close! ws status-code reason): Initiates an orderly closure by sending a Close message.
    • (abort! ws): Closes input and output abruptly.
    ;; Example of sending data
    (send! ws "Hello WebSocket!")
    
    ;; Example of orderly closure
    (close! ws 1000 "Normal Closure")