(l/script- :js
  {:runtime :basic
   :require [[xt.net.http-util :as util]
             [xt.net.http-fetch :as fetch]
             [xt.lang.spec-base :as xt]]})

(fact:global
 {:setup [(l/rt:restart)]
  :teardown [(l/rt:stop)]})

xt.net

HTTP, websocket, SQL, Redis, and Supabase helpers.

xt.net contains portable networking adapters used by generated programs and substrate/database systems.

1    Motivation

Generated xtalk programs need consistent wrappers around fetch, websocket transports, Phoenix websocket conventions, SQL connections, Redis connections, and Supabase addons.

2    Walkthrough

2.1    Request and response envelopes

xt.net.http-util converts between Clojure data and the wire format used by portable fetch clients.

encode and decode request bodies

^{:refer xt.net.http-util/request-body :added "4.0"}
(!.js
  [(util/request-body nil)
   (util/request-body "plain-text")
   (util/request-body {"id" "ord-1"})])
=> [nil "plain-text" "{\"id\":\"ord-1\"}"]

^{:refer xt.net.http-util/decode-body :added "4.0"}
(!.js
  [(util/decode-body "{\"id\":\"ord-1\"}")
   (util/decode-body "plain-text")
   (util/decode-body nil)])
=> [{"id" "ord-1"} "plain-text" nil]

^{:refer xt.net.http-util/encode-query-params :added "4.0"}
(!.js
  (util/encode-query-params {"a" 1
                             "b" nil
                             "c" "two"}))
=> "a=1&c=two"

2.2    Response normalisation

xt.net.http-util/response-normalize turns raw fetch responses into a predictable map, decoding JSON bodies automatically.

normalise a raw fetch response

^{:refer xt.net.http-util/response-normalize :added "4.0"}
(!.js
  (util/response-normalize {"status" 200
                            "headers" {"content-type" "application/json"}
                            "body" "{\"id\":\"ord-1\"}"
                            "error" nil}))
=> {"status" 200
    "headers" {"content-type" "application/json"}
    "body" {"id" "ord-1"}
    "error" nil}

2.3    Fetch preparation

xt.net.http-fetch builds complete request inputs from host defaults and per-request options.

prepare url and input for a fetch client

^{:refer xt.net.http-fetch/prepare-url :added "4.0"}
(!.js
  (fetch/prepare-url
   {:defaults {:secured false
               :host "127.0.0.1"
               :port "55121"
               :basepath "/auth/v1"}}
   {:path "/sign-in"}))
=> "http://127.0.0.1:55121/auth/v1/sign-in"

^{:refer xt.net.http-fetch/prepare-input :added "4.0"}
(!.js
  (fetch/prepare-input
   {:defaults {:secured false
               :host "127.0.0.1"
               :port "55121"
               :headers {"apikey" "TOKEN"}
               :basepath "/auth/v1"}}
   {:path "/sign-in"}))
=> {"url" "http://127.0.0.1:55121/auth/v1/sign-in"
    "method" "GET"
    "headers" {"apikey" "TOKEN"}}

2.4    End-to-end: build a request envelope

Combining the helpers above gives a predictable request map ready for a target fetch implementation.

construct a full GET request

(!.js
  (var input (fetch/prepare-input
              {:defaults {:secured true
                          :host "api.example.com"
                          :port "443"
                          :headers {"apikey" "TOKEN"}
                          :basepath "/v1"}}
              {:path "/items"
               :query {"limit" 10}}))
  [(. input ["url"])
   (. input ["method"])
   (. input ["headers" "apikey"])])
=> ["https://api.example.com:443/v1/items?limit=10" "GET" "TOKEN"]

3    Internal usage

The networking layer is used by xt.db, xt.substrate transports, and Supabase-related examples. Tests under test-lang/xt/net provide runnable behavior examples.

4    API