Pull-based remote protocol over HTTP.
(require '[sg.flybot.pullable.remote :as remote])
;; Define API as a function: request → {:data ... :schema ...}
(defn my-api [ring-request]
{:data {:user {:name "Alice" :age 30}}
:schema {:user {:name :string :age :number}}})
;; Create Ring handler
(def handler (remote/make-handler my-api))
(require '[sg.flybot.pullable.remote.client :as client])
(def api (client/connect "http://localhost:8080/api"))
(api '{:user {:name ?n}})
;; => {'n "Alice"}
(client/schema api) ; introspect
POST /api - Execute pull patternGET /api/_schema - Schema introspection (session-aware)Content negotiation via Accept/Content-Type headers:
application/transit+json (default)application/transit+msgpackapplication/ednRequest: {:pattern '{:user {:name ?n}}}
Success: {'n "Alice"}
Failure: {:errors [{:code :schema-violation :reason "..."}]}
Pull-based remote protocol over HTTP.
## Server Quick Start
```clojure
(require '[sg.flybot.pullable.remote :as remote])
;; Define API as a function: request → {:data ... :schema ...}
(defn my-api [ring-request]
{:data {:user {:name "Alice" :age 30}}
:schema {:user {:name :string :age :number}}})
;; Create Ring handler
(def handler (remote/make-handler my-api))
```
## Client Quick Start (JVM only)
```clojure
(require '[sg.flybot.pullable.remote.client :as client])
(def api (client/connect "http://localhost:8080/api"))
(api '{:user {:name ?n}})
;; => {'n "Alice"}
(client/schema api) ; introspect
```
## Endpoints
- `POST /api` - Execute pull pattern
- `GET /api/_schema` - Schema introspection (session-aware)
## Wire Format
Content negotiation via Accept/Content-Type headers:
- `application/transit+json` (default)
- `application/transit+msgpack`
- `application/edn`
## Request/Response
Request: `{:pattern '{:user {:name ?n}}}`
Success: `{'n "Alice"}`
Failure: `{:errors [{:code :schema-violation :reason "..."}]}`Encode Clojure data to bytes. Format: :transit-json, :transit-msgpack, :edn.
Encode Clojure data to bytes. Format: :transit-json, :transit-msgpack, :edn.
Execute a pull pattern directly (no HTTP). Used by in-process callers like browser sandboxes that share the same execution engine as the server.
api-fn: (fn [context] {:data ... :schema ... :errors ...}) pattern: Clojure data structure (EDN) opts: {:params {...} ; $-param substitution :resolve fn ; symbol resolver (default: safe whitelist) :eval-fn fn ; form evaluator (default: blocked) :context map} ; passed to api-fn
Returns vars map on success, {:errors [...]} on failure.
(def api-fn
(fn [_ctx] {:data {:posts posts-coll}}))
(execute api-fn '{:posts ?all})
;; => {'all [...]}
Execute a pull pattern directly (no HTTP). Used by in-process callers
like browser sandboxes that share the same execution engine as the server.
api-fn: (fn [context] {:data ... :schema ... :errors ...})
pattern: Clojure data structure (EDN)
opts: {:params {...} ; $-param substitution
:resolve fn ; symbol resolver (default: safe whitelist)
:eval-fn fn ; form evaluator (default: blocked)
:context map} ; passed to api-fn
Returns vars map on success, {:errors [...]} on failure.
```clojure
(def api-fn
(fn [_ctx] {:data {:posts posts-coll}}))
(execute api-fn '{:posts ?all})
;; => {'all [...]}
```Create a Ring handler for pull-based API.
Arguments:
The api-fn returns :errors config for error handling:
Collections return errors as data: {:error {:type :forbidden :message "..."}}
Options:
Example:
(def handler
(make-handler
(fn [req]
{:data (build-api (:session req))
:schema my-schema
:errors {:detect :error
:codes {:forbidden 403}}})))
Create a Ring handler for pull-based API.
Arguments:
- api-fn: Function (ring-request) → {:data lazy-map, :schema schema-map, :errors errors-config}
The api-fn returns :errors config for error handling:
- :detect - keyword or fn to detect errors in mutation results
- :codes - Map of error-type to HTTP status
Collections return errors as data: {:error {:type :forbidden :message "..."}}
Options:
- :path - Base path for API (default "/api")
Example:
```clojure
(def handler
(make-handler
(fn [req]
{:data (build-api (:session req))
:schema my-schema
:errors {:detect :error
:codes {:forbidden 403}}})))
```Detect if pattern is a mutation. Returns {:path :query :value} or nil.
Mutations use nil (create) or map (update/delete) as query keys. Read patterns (keyword query keys or ?-variable values) return nil.
Detect if pattern is a mutation. Returns {:path :query :value} or nil.
Mutations use nil (create) or map (update/delete) as query keys.
Read patterns (keyword query keys or ?-variable values) return nil.(wrap-api handler api-fn)(wrap-api next-handler api-fn {:keys [path] :or {path "/api"} :as opts})Ring middleware that adds pull API at specified path.
Delegates non-API requests to the wrapped handler.
Options:
(def app
(-> my-handler
(wrap-api my-api {:path "/api/v1"})))
Ring middleware that adds pull API at specified path.
Delegates non-API requests to the wrapped handler.
Options:
- :path - Base path for API (default "/api")
```clojure
(def app
(-> my-handler
(wrap-api my-api {:path "/api/v1"})))
```cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |