CRUD collection abstraction — wraps any data source in a uniform interface.
Implement the DataSource protocol for your storage layer (database, API, etc.),
then wrap it with collection to get ILookup, Seqable, Counted, Mutable, and
Wireable. One DataSource, one Collection, then compose behavior with wrappers:
read-only — disables mutations, delegates readswrap-mutable — custom mutation logic (auth, ownership), delegates readsvalidated — Malli validation of mutation input, delegates readslookup — non-enumerable keyword->value ILookup with lazy delay support;; 1. Implement DataSource for your storage
(defrecord MySource [conn]
DataSource
(fetch [_ query] ...)
(list-all [_] ...)
(create! [_ data] ...)
(update! [_ query data] ...)
(delete! [_ query] ...))
;; 2. Wrap in a Collection
(def posts (collection (->MySource conn) {:id-key :post/id}))
;; 3. Use standard Clojure verbs
(get posts {:post/id 3}) ; fetch by query (ILookup)
(seq posts) ; list all (Seqable)
(mutate! posts nil {:title "New"}) ; create
(mutate! posts {:post/id 3} data) ; update
(mutate! posts {:post/id 3} nil) ; delete
;; 4. Compose wrappers for different access levels
(def public (read-only posts))
(def restricted (wrap-mutable posts auth-fn))
For in-memory use, atom-source provides a built-in DataSource
with auto-incrementing IDs and transactional batch mutations via
transact! / snapshot.
CRUD collection abstraction — wraps any data source in a uniform interface.
Implement the DataSource protocol for your storage layer (database, API, etc.),
then wrap it with `collection` to get ILookup, Seqable, Counted, Mutable, and
Wireable. One DataSource, one Collection, then compose behavior with wrappers:
- `read-only` — disables mutations, delegates reads
- `wrap-mutable` — custom mutation logic (auth, ownership), delegates reads
- `validated` — Malli validation of mutation input, delegates reads
- `lookup` — non-enumerable keyword->value ILookup with lazy delay support
## Basic Usage
```clojure
;; 1. Implement DataSource for your storage
(defrecord MySource [conn]
DataSource
(fetch [_ query] ...)
(list-all [_] ...)
(create! [_ data] ...)
(update! [_ query data] ...)
(delete! [_ query] ...))
;; 2. Wrap in a Collection
(def posts (collection (->MySource conn) {:id-key :post/id}))
;; 3. Use standard Clojure verbs
(get posts {:post/id 3}) ; fetch by query (ILookup)
(seq posts) ; list all (Seqable)
(mutate! posts nil {:title "New"}) ; create
(mutate! posts {:post/id 3} data) ; update
(mutate! posts {:post/id 3} nil) ; delete
;; 4. Compose wrappers for different access levels
(def public (read-only posts))
(def restricted (wrap-mutable posts auth-fn))
```
For in-memory use, `atom-source` provides a built-in DataSource
with auto-incrementing IDs and transactional batch mutations via
`transact!` / `snapshot`.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 |