Liking cljdoc? Tell your friends :D

Fulcro RAD Datalevin

A database adapter plugin for Fulcro RAD that provides support for Datalevin (1.0.0), an embedded Datalog database.

Features

  • Automatic schema generation from RAD attributes

  • Save and delete middleware for RAD forms

  • Works with both Pathom 2 and Pathom 3 — the adapter has no hard dependency on either; you bring your own

  • Automatic resolver generation (id-resolvers, all-ids resolvers, full-text search resolvers, and vector similarity resolvers)

  • Full-text search: declare ::dlo/fulltext? on an attribute and get schema, search-domain config, and a relevance-ordered :<entity>/search resolver automatically

  • Vector similarity search: declare a :vec attribute and get an :<entity>/similar nearest-neighbor resolver automatically — pair with full-text for hybrid keyword + semantic search

  • Read-your-writes: a form save’s mutation returns the just-saved entity within the same request

  • Support for all RAD attribute types, including :enum and :vec (vector / HNSW index)

  • Native :db/id identity support (::dlo/native-id?)

  • Connection and database management utilities

  • Datalevin 1.0.0 capabilities surfaced through the adapter:

    • :conn-opts pass-through (:auto-entity-time?, :validate-data?, :closed-schema?, :wal?, :search-domains, …)

    • Database-side attribute predicates via native :db.attr/preds

    • Transaction post-conditions via :db/ensure (::dlo/raw-txn / append-to-raw-txn)

    • Schema verification (schema-problems / verify-schema!)

    • Per-transaction timeouts via with-transaction (::dlo/transaction-timeout-ms)

  • Production-ready error handling with detailed context (failures propagate; they are never swallowed)

  • Configurable batch-size safety limit

  • Input validation with helpful error messages

Installation

Add to your deps.edn:

{:deps {us.whitford/fulcro-rad-datalevin {:git/url "https://github.com/michaelwhitford/fulcro-rad-datalevin"
                                           :git/sha "LATEST_SHA"}}}

Or use a local checkout:

{:deps {us.whitford/fulcro-rad-datalevin {:local/root "/path/to/fulcro-rad-datalevin"}}}
This library does not bundle Pathom. Add the Pathom version your app uses to your own deps.edncom.wsscode/pathom (Pathom 2) or com.wsscode/pathom3 (Pathom 3). generate-resolvers returns Pathom-2-shape resolver maps, which work directly in a Pathom 2 parser and are auto-converted by RAD’s Pathom 3 processor (see 3. Configure Pathom Parser).

Quick Start

1. Define RAD Attributes

(ns com.example.model.account
  (:require
    [com.fulcrologic.rad.attributes :as attr :refer [defattr]]
    [us.whitford.fulcro.rad.database-adapters.datalevin-options :as dlo]))

(defattr id :account/id :uuid
  {::attr/schema     :main
   ::attr/identity?  true})

(defattr name :account/name :string
  {::attr/schema      :main
   ::attr/identities  #{:account/id}
   ::attr/required?   true})

(defattr email :account/email :string
  {::attr/schema      :main
   ::attr/identities  #{:account/id}
   ;; Merge native Datalevin schema keys via ::dlo/attribute-schema
   ::dlo/attribute-schema {:db/unique :db.unique/value}})

;; Enum attributes
(defattr role :account/role :enum
  {::attr/schema             :main
   ::attr/identities         #{:account/id}
   ::attr/enumerated-values  #{:admin :user :guest}
   ::attr/enumerated-labels  {:admin "Administrator"
                              :user  "Regular User"
                              :guest "Guest User"}})

(defattr status :account/status :enum
  {::attr/schema             :main
   ::attr/identities         #{:account/id}
   ;; Can use fully-qualified keywords
   ::attr/enumerated-values  #{:status/active :status/inactive}})

(defattr permissions :account/permissions :enum
  {::attr/schema             :main
   ::attr/identities         #{:account/id}
   ::attr/cardinality        :many  ;; Multiple values allowed
   ::attr/enumerated-values  #{:read :write :execute}})

(def attributes [id name email role status permissions])
When querying enum attributes directly with d/pull, use pull patterns with :db/ident to get the keyword value: (d/pull db [:account/id {:account/role [:db/ident]}] [:account/id id]) returns {:account/id uuid :account/role {:db/ident :account.role/admin}}. The auto-generated resolvers do this mapping for you and return the plain keyword (:account.role/admin).

2. Start Database Connection

(ns com.example.components.database
  (:require
    [us.whitford.fulcro.rad.database-adapters.datalevin :as dl]
    [com.example.model.account :as account]))

(defonce connections (atom {}))

(defn start! []
  (let [conn (dl/start-database!
               {:path       "/var/data/my-app"
                :schema     :main
                :attributes account/attributes
                ;; optional: native Datalevin get-conn options (Datalevin 1.0.0)
                :conn-opts  {:auto-entity-time? true
                             :validate-data?    true}})]
    (swap! connections assoc :main conn)))

(defn stop! []
  (doseq [[_ conn] @connections]
    (dl/stop-database! conn))
  (reset! connections {}))

3. Configure Pathom Parser

The adapter works with both Pathom 2 and Pathom 3. generate-resolvers returns Pathom-2-shape resolver maps (plain data, no Pathom dependency needed to build them); they register directly in a Pathom 2 parser and are auto-converted by RAD’s Pathom 3 processor.

Two pieces wire the adapter into the parser env, per Pathom version:

  • Pathom 2: dl/pathom-plugin — a ::p/wrap-parser plugin.

  • Pathom 3: dl/wrap-env — an (fn [env] env') you compose into the processor’s env-middleware.

Both inject ::dlo/connections and the atom-backed ::dlo/databases snapshot into the env (see Read-your-writes).

Pathom 2

(ns com.example.components.parser
  (:require
    [com.fulcrologic.rad.attributes :as attr]
    [com.fulcrologic.rad.form :as form]
    [com.fulcrologic.rad.pathom :as pathom]           ;; you provide com.wsscode/pathom
    [us.whitford.fulcro.rad.database-adapters.datalevin :as dl]
    [com.example.model.account :as account]
    [com.example.components.database :as db]))

(def all-attributes
  (vec (concat account/attributes
               ;; other model attributes
               )))

(def parser
  (pathom/new-parser {}
    [(attr/pathom-plugin all-attributes)
     (form/pathom-plugin (dl/wrap-datalevin-save) (dl/wrap-datalevin-delete))
     ;; dl/pathom-plugin takes a (fn [env]) returning the schema -> connection map
     (dl/pathom-plugin (fn [_env] @db/connections))]
    ;; generate-resolvers returns Pathom-2-shape maps; add RAD's form mutations
    [(dl/generate-resolvers all-attributes :main)
     form/resolvers]))

;; Run a query: (parser {} [{[:account/id id] [:account/name]}])

Pathom 3

(ns com.example.components.parser
  (:require
    [com.fulcrologic.rad.attributes :as attr]
    [com.fulcrologic.rad.form :as form]
    [com.fulcrologic.rad.pathom3 :as pathom3]          ;; you provide com.wsscode/pathom3
    [us.whitford.fulcro.rad.database-adapters.datalevin :as dl]
    [com.example.model.account :as account]
    [com.example.components.database :as db]))

(def all-attributes (vec (concat account/attributes)))

(def processor
  (pathom3/new-processor {}
    ;; env-middleware: compose attr, form, and the adapter's wrap-env
    (-> (attr/wrap-env all-attributes)
        (form/wrap-env (dl/wrap-datalevin-save) (dl/wrap-datalevin-delete))
        (dl/wrap-env (fn [_env] @db/connections)))
    []                                                 ;; extra Pathom 3 plugins
    ;; new-processor auto-converts the Pathom-2-shape resolvers for you
    [(dl/generate-resolvers all-attributes :main)
     form/resolvers]))

;; Run a query: (processor {} [{[:account/id id] [:account/name]}])
If you build a Pathom 3 index yourself (pci/register) instead of using new-processor, convert the resolvers first — either call dl/generate-resolvers-pathom3 (native Pathom 3 records) or run RAD’s com.fulcrologic.rad.pathom3/convert-resolvers on generate-resolvers output.

4. Configure Form Middleware

(ns com.example.components.middleware
  (:require
    [us.whitford.fulcro.rad.database-adapters.datalevin :as dl]
    [com.fulcrologic.rad.middleware.save-middleware :as save-mw]))

;; Middleware wraps a base handler. Use the 1-arity form to compose,
;; or the 0-arity form as a terminal handler.
(def save-middleware
  (-> (dl/wrap-datalevin-save)          ;; terminal
      (save-mw/wrap-rewrite-values)))

(def delete-middleware
  (dl/wrap-datalevin-delete))           ;; terminal

;; Composing over your own handler:
(def save-middleware-composed
  (dl/wrap-datalevin-save my-base-save-handler))

Schema is determined from each attribute’s ::attr/schema; the middleware does not take an options map.

API Reference

Schema Generation

automatic-schema

Generate a Datalevin schema (map-of-maps) from RAD attributes

(dl/automatic-schema :main account/attributes)
;; => {:account/id    {:db/valueType :db.type/uuid
;;                     :db/unique    :db.unique/identity}
;;     :account/name  {:db/valueType :db.type/string}
;;     :account/email {:db/valueType :db.type/string
;;                     :db/unique    :db.unique/value}}
ensure-schema!

Ensure the connection’s schema matches the RAD-derived schema (updates if needed)

(dl/ensure-schema! conn schema-map)
schema-problems

Compare the RAD-derived expected schema against a connection’s live schema; returns a seq of :missing / :mismatch problem maps for adapter-managed keys (:db/valueType, :db/cardinality, :db/unique).

(dl/schema-problems conn :main account/attributes)
;; => () when the live schema satisfies the expected schema
verify-schema!

Like schema-problems, but throws ex-info when problems exist and returns true otherwise.

(dl/verify-schema! conn :main account/attributes)

Database Management

start-database!

Start a database connection with automatic schema. Config keys: :path, :schema, :attributes, :auto-schema? (default true), :conn-opts (optional native get-conn options; merged with adapter-derived :vector-domains).

(dl/start-database!
  {:path        "/var/data/mydb"
   :schema      :main
   :attributes  all-attributes
   :auto-schema? true
   :conn-opts   {:auto-entity-time? true
                 :closed-schema?    true}})
start-databases

Start several schemas at once. Takes a config holding ::dlo/databases (schema → per-database config) and an options map carrying the shared :attributes.

(dl/start-databases
  {::dlo/databases {:main    {:path "/var/data/main"
                              :conn-opts {:auto-entity-time? true}}
                    :reports {:path "/var/data/reports"}}}
  {:attributes all-attributes})
;; => {:main conn :reports conn}
stop-database! / stop-databases

Close connection(s)

(dl/stop-database! conn)
(dl/stop-databases connections-map)
seed-database!

Seed a database with initial data (raw transaction)

(dl/seed-database! conn [{:account/id id :account/name "Alice"}])
Temporary/throwaway databases for tests are provided by the test-only helpers with-test-conn / with-test-conn-attrs and mock-resolver-env in …​database-adapters.test-utils (test source), not the main public API.

Middleware

wrap-datalevin-save

Form save middleware that transacts deltas to Datalevin. Arities: ([]) terminal, ([handler]) composing.

(def save-middleware (dl/wrap-datalevin-save))          ;; terminal
(def save-middleware (dl/wrap-datalevin-save handler))  ;; compose
wrap-datalevin-delete

Form delete middleware. Arities: ([]) terminal, ([handler]) composing.

(def delete-middleware (dl/wrap-datalevin-delete))
append-to-raw-txn

From your own save middleware, append native Datalevin transaction forms (e.g. [:db/ensure …​] post-conditions) to a save.

(dl/append-to-raw-txn env [[:db/ensure `my.app.rules/balance-non-negative? [:account/id id]]])

Resolver Generation

generate-resolvers

Generate resolvers from attributes for a schema (schema arg required). Produces an id-resolver and an all-ids resolver per identity. Returns Pathom-2-shape resolver maps (plain data keyed by :com.wsscode.pathom.connect/{sym,input,output,batch?,resolve}) — these register directly in a Pathom 2 parser and are auto-converted by RAD’s Pathom 3 new-processor.

(def resolvers (dl/generate-resolvers all-attributes :main))
generate-resolvers-pathom3

Like generate-resolvers, but returns native Pathom 3 resolver records. Use only when you build a Pathom 3 index directly (e.g. pci/register) rather than via new-processor. Pathom 3 is resolved lazily at call time, so it stays optional.

(def resolvers (dl/generate-resolvers-pathom3 all-attributes :main))
get-by-ids

Fetch multiple entities by their identity attribute values

(dl/get-by-ids db :account/id [id1 id2] [:account/name :account/email])
;; => {id1 {:account/name "Alice" ...} id2 {...}}

Native-id helpers: native-id?, pathom-query→datalevin-query, datalevin-result→pathom-result support attributes that map onto Datalevin’s internal :db/id (see ::dlo/native-id?).

Pathom Integration

The adapter is Pathom-version-agnostic. Use pathom-plugin for Pathom 2 and wrap-env for Pathom 3; both inject ::dlo/connections and the atom-backed ::dlo/databases snapshot into the env. See 3. Configure Pathom Parser for full wiring.

pathom-plugin

Create a Pathom 2 plugin (a :com.wsscode.pathom.core/wrap-parser map) that injects ::dlo/connections and ::dlo/databases into the parse env. Pass a (fn [env]) returning the schema → connection map.

(dl/pathom-plugin (fn [_env] @connections))
wrap-env

The Pathom 3 integration point: an (fn [env] env') that injects ::dlo/connections and ::dlo/databases. Compose it into the env-middleware you pass to RAD’s new-processor (it also underlies the Pathom 2 pathom-plugin).

(-> (attr/wrap-env all-attributes)
    (form/wrap-env save-mw delete-mw)
    (dl/wrap-env (fn [_env] @connections)))

Read-your-writes

::dlo/databases holds {schema → atom<db>} — an atom per schema, seeded by pathom-plugin / wrap-env at the start of each request. After a successful save or delete, the middleware publishes the transaction report’s :db-after into the relevant atom, so resolvers that run later in the same Pathom request see the write. In practice this means a form/save-form mutation returns the just-saved (or updated) entity, not only its :tempids — matching the Datomic and XTDB adapters. This uses Datalevin’s native :db-after for read-your-writes plus request-scoped snapshot consistency. Resolvers deref the snapshot leniently, so a bare db value in a hand-built env still works.

Query Utilities

q

Execute Datalog queries

(dl/q '[:find ?e ?name
        :where [?e :account/name ?name]]
      db)
pull

Pull entity data

(dl/pull db [:account/name :account/email] eid)
pull-many

Pull multiple entities

(dl/pull-many db [:account/name] eids)

Delta Utilities

Lower-level helpers used by the save middleware, re-exported for advanced use: delta→txn, keys-in-delta, schemas-for-delta, save-form!.

Configuration Options

The datalevin-options namespace (::dlo/*) provides configuration keys:

(require '[us.whitford.fulcro.rad.database-adapters.datalevin-options :as dlo])

;; Environment keys (injected by pathom-plugin (Pathom 2) / wrap-env (Pathom 3))
::dlo/connections   ; Map of schema -> connection
::dlo/databases     ; Map of schema -> atom<db> (per-request snapshot; save/delete
                    ; publish :db-after here for read-your-writes)

;; Attribute-level options
::dlo/attribute-schema     ; Merge/override native Datalevin schema keys
                           ; (e.g. :db/unique, :db.attr/preds)
::dlo/native-id?           ; Use Datalevin's internal :db/id (attribute must be :long)
::dlo/generate-resolvers?  ; If false, skip automatic resolver generation
::dlo/wrap-resolve         ; (fn [resolve]) => (fn [env input]) wrapping an id-resolver
::dlo/schema               ; Schema name for this attribute

;; Save-env keys
::dlo/transact-options       ; Passed as tx-meta (3rd arg) to transact!
::dlo/raw-txn                ; Vector of native txn forms to append (e.g. :db/ensure)
::dlo/transaction-timeout-ms ; Per-txn timeout via with-transaction (Datalevin 1.0.0)

;; Resolver-env key
::dlo/max-batch-size         ; Max entities per batch query (default: 1000)

Database-Side Validation & Post-Conditions (Datalevin 1.0.0)

Attribute predicates (:db.attr/preds) enforce database-side value validation. The predicate must be a qualified symbol (the schema is persisted and resolved via requiring-resolve), is invoked as (pred value), and must return strictly true — any other result aborts the write with a :transact/attr-pred error:

;; my.app.validation
(defn valid-email? [v] (boolean (re-matches #".+@.+\..+" v)))

(defattr email :account/email :string
  {::attr/identity?       true
   ::dlo/attribute-schema {:db.attr/preds 'my.app.validation/valid-email?}})

For transaction-level assertions (spanning attributes/entities), append a [:db/ensure pred & args] form via ::dlo/raw-txn / append-to-raw-txn. The predicate runs against db-after and aborts the transaction on any falsey result.

RAD attributes of type :vec map to :db.type/vec and build a Datalevin HNSW vector index. The index configuration (:dimensions, :metric-type) lives on the connection, not the schema; the adapter derives it automatically and passes :vector-domains to d/get-conn. Put :db.vec/dimensions (and optionally :db.vec/metric-type) in the attribute’s ::dlo/attribute-schema and the adapter strips them from the schema and moves them to the connection options:

(defattr embedding :account/embedding :vec
  {ao/identities            #{:account/id}
   ::dlo/attribute-schema   {:db.vec/dimensions  768
                             :db.vec/metric-type :cosine}})

Every entity type with at least one :vec attribute automatically gets an :<entity>/similar resolver — parameterized nearest-neighbor search returning idents in similarity order (nearest first), with columns filled by the batched id-resolver:

;; EQL (what a load/report sends):
[{(:account/similar {:vector query-embedding :top 10}) [:account/id :account/name]}]
;; => {:account/similar [{:account/id #uuid "..." :account/name "..."} ...]}

Parameters (read from (:query-params env)):

ParamMeaning

:vector

Required. The query embedding — a sequence of numbers matching the attribute’s :db.vec/dimensions. Missing resolves to an empty list.

:attribute

Optional qualified keyword narrowing the search to one :vec attribute (e.g. :account/embedding). Defaults to all of the entity’s vector domains (which must then share dimensions).

:top

Cap on neighbors returned (Datalevin default 10).

Vector indexing is synchronous — saved entities are immediately searchable. Native-id entities are supported (the matched eid is the id).

Hybrid search: combine :<entity>/search (keyword relevance) and :<entity>/similar (semantic similarity) over the same entities — issue both loads and merge/re-rank client-side, or use two report sections.

Full-Text Search (::dlo/fulltext?)

Mark any attribute searchable and the adapter wires the whole feature — schema, connection configuration, and a generated, parameterized search resolver:

(defattr name :account/name :string
  {ao/identities   #{:account/id}
   ::dlo/fulltext? true})

;; Phrase/proximity search needs positional indexing (opt-in — costs storage):
(defattr bio :account/bio :string
  {ao/identities   #{:account/id}
   ::dlo/fulltext? {:index-position? true}})

This produces:

  • Schema:db/fulltext true plus a derived :db.fulltext/domains ["account"]: one shared search domain per entity type (named after the attribute namespace), so all searchable attributes of an entity are searched together. Hand-written :db.fulltext/domains / :db.fulltext/autoDomain in ::dlo/attribute-schema are respected and suppress derivation.

  • Connection — map-valued options (like :index-position?) flow into the :search-domains option passed to d/get-conn by start-database!.

  • Resolvergenerate-resolvers emits an :account/search resolver returning idents in relevance order (descending score); columns are filled by the existing batched id-resolver:

;; EQL (what a load/report sends):
[{(:account/search {:query "fox" :top 20}) [:account/id :account/name]}]
;; => {:account/search [{:account/id #uuid "..." :account/name "Fox Fox LLP"} ...]}

Parameters (read from (:query-params env), which both RAD parsers populate from the load’s {:params …​}):

ParamMeaning

:query

Required. A string, a boolean expression vector such as [:and "red" [:not "dog"]], or {:phrase "quick red fox"} (phrase search requires {:index-position? true} on the attribute). Missing/blank resolves to an empty list.

:top

Cap on engine results (Datalevin default 10).

:limit / :offset

Pagination within the result window.

Wiring a RAD report is just a source attribute and a control — the control’s value travels as the :query param:

(report/defsc-report AccountSearchList [this props]
  {ro/source-attribute :account/search
   ro/row-pk           account/id
   ro/columns          [account/name account/email]
   ro/run-on-mount?    false
   ro/controls         {:query {:type     :string
                                :label    "Search"
                                :onChange (fn [this _] (control/run! this))}}})

Notes:

  • Indexing is synchronous — saved entities are immediately searchable (read-your-writes).

  • Native-id entities are supported: the matched entity id is returned directly.

  • Datalevin stores only references to the source text (no double storage).

Type Mapping

RAD types are automatically mapped to Datalevin types:

RAD TypeDatalevin Type

:string

:db.type/string

:password

:db.type/string

:boolean

:db.type/boolean

:int

:db.type/long

:long

:db.type/long

:double

:db.type/double

:float

:db.type/float

:bigdec

:db.type/bigdec

:instant

:db.type/instant

:keyword

:db.type/keyword

:symbol

:db.type/symbol

:uuid

:db.type/uuid

:enum

:db.type/ref

:ref

:db.type/ref

:tuple

:db.type/tuple

:vec

:db.type/vec

Numeric values are coerced to the declared RAD type on save (fix-numerics): :int/:longlong, :double/:floatdouble, :bigdecbigdec. This handles JavaScript clients that transmit integers where doubles are expected (and vice versa), avoiding :db.type mismatch errors.

Error Handling

The adapter provides structured error handling with detailed context. Save and delete transaction failures are always propagated (never swallowed) so attribute-predicate, :db/ensure, and other transaction errors reach the caller/client.

Missing Connection Errors

When a database connection is not configured for a schema, an ex-info exception is thrown with:

  • :schema - the requested schema key

  • :available-schemas - vector of configured schema keys

Transaction Failures

Failed save/delete transactions throw ex-info with context, e.g.:

  • save: :schema, :txn-data

  • delete: :ident, :schema

Input Validation

Invalid delta structures are rejected with descriptive errors:

  • Delta must be a map

  • Each entry must have [id-attr id] as key

  • Each change must have :before and :after keys

Batch Size Limits

Batch queries exceeding the max batch size (default 1000, override with ::dlo/max-batch-size in the resolver env) throw with :requested, :maximum, and :id-attr context.

Differences from Datomic Adapter

Key differences from fulcro-rad-datomic:

  • Schema format: Datalevin uses a map-of-maps schema format, not transaction vectors

  • No temporal database: Datalevin doesn’t maintain history by default

  • Embedded database: No separate transactor process needed

  • Connection management: Uses get-conn instead of connect

  • Schema updates: Schema passed at connection time, updated via update-schema

  • Vector search: Native :db.type/vec / HNSW index via :vector-domains

  • Namespace: Uses us.whitford.fulcro.rad.database-adapters instead of com.fulcrologic.rad.database-adapters

Development

Running Tests

clojure -M:run-tests

Focus a single namespace:

clojure -M:run-tests --focus <test-ns>

Linting

clj-kondo --lint src/main src/test

Building / Installing / Deploying

Builds use tools.build (see build.clj). The version defaults to the value in build.clj; set the VERSION env var to override (CI derives it from the pushed git tag).

clojure -T:build jar       # write pom + build thin jar into target/
clojure -T:build install   # install to local ~/.m2
clojure -X:deploy          # deploy target/ jar to Clojars (separate step;
                           # needs CLOJARS_USERNAME + CLOJARS_PASSWORD)

Releases are automated: pushing a v1.2.3 (full release) or v1.2.3-RC1 (release candidate) tag triggers the GitHub Actions release workflow, which runs the tests, deploys to Clojars, and creates a GitHub Release. -alpha / -beta version suffixes are for local builds only and never trigger a deploy.

Troubleshooting

Forms Don’t Save or Revert After Save

Symptoms: Form appears to save but immediately reverts, or changes don’t persist to database.

Common Causes:

  1. Middleware not properly configured - Ensure the save/delete middleware is in your form handler chain:

    (def save-middleware
      (-> (dl/wrap-datalevin-save)
          (save-mw/wrap-rewrite-values)))
  2. Missing database connection - Verify the Pathom env has ::dlo/connections. This is injected from the (fn [env]) you provide to dl/pathom-plugin (Pathom 2) or dl/wrap-env (Pathom 3).

  3. Schema mismatch - Ensure each attribute’s ::attr/schema matches a key in your connections map:

    ;; Attribute defines :main
    (defattr id :account/id :uuid {::attr/schema :main})
    
    ;; Connection must use :main
    (swap! connections assoc :main conn)

Debug Steps:

  1. Run the test suite to verify the adapter works: clojure -M:run-tests

  2. If tests pass, the issue is in your app configuration

  3. tap> the middleware result and confirm it includes a :tempids key

  4. Check server logs for connection or transaction errors (they are no longer swallowed)

"No database connection configured for schema"

Cause: Your attributes reference a schema that doesn’t have a connection.

Solution: Add the missing connection so every ::attr/schema used has a matching key in the connections map.

Batch Size Limit Errors

Cause: Trying to fetch more than the max batch size (default 1000) in a single query.

Solution: Paginate, or raise the limit via the resolver env:

{::dlo/max-batch-size 5000}

License

Copyright (c) Michael Whitford

Distributed under the MIT License.

Can you improve this documentation? These fine people already did:
Michael Whitford & Claude
Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close