Yes, that’s correct - you would need to use the peer API to install your transaction function(s), but you can use them from the Client API once they’re installed:
;; In a REPL that has the Peer API in the classpath,
;; create, install, and test the txn function:
(require
'[datomic.api :as d])
(def uri "datomic:dev://localhost:4334/tx-fn-test")
(d/create-database uri)
(def conn (d/connect uri))
(def ensure-composite
(d/function
'{:lang "clojure"
:params [db k1 v1 k2 v2]
:code (if-let [[e t1 t2] (d/q '[:find [?e ?t1 ?t2]
:in $ ?k1 ?v1 ?k2 ?v2
:where
[?e ?k1 ?v1 ?t1]
[?e ?k2 ?v2 ?t2]]
db k1 v1 k2 v2)]
(throw (ex-info (str "Entity already exists " e)
{:e e :t (d/tx->t (max t1 t2))}))
[{:db/id (d/tempid :db.part/user)
k1 v1
k2 v2}])}))
;; install a transaction function
@(d/transact conn [{:db/id #db/id [:db.part/user]
:db/ident :examples/ensure-composite
:db/doc "Create an entity with k1=v1, k2=v2, throwing if such an entity already exists"
:db/fn ensure-composite}])
(def db (d/db conn))
;; test locally
(d/invoke db :examples/ensure-composite db
:db/ident :example/object :db/doc "Example object")
(def tx-data [[:examples/ensure-composite :db/ident :example/object :db/doc "Example object"]])
;; first ensure wins
@(d/transact conn tx-data)
;; second ensure throws exception including t at which entity is known
(try
@(d/transact conn tx-data)
(catch Exception e
(println "Got expected exception " (.getMessage e))
(def t (:t (ex-data (.getCause e))))))
;;; End Peer REPL session ;;;
;;;; From a console run the peer server:
; bin/run -m datomic.peer-server -a k,s -d test,datomic:dev://localhost:4334/tx-fn-test
;;; In a REPL that has the Client API in the classpath,
;;; You can use the installed txn function:
(require '[datomic.client.api :as d])
(def client
(d/client
{:server-type :peer-server
:endpoint "localhost:8998"
:secret "s"
:access-key "k"
:validate-hostnames false}))
(def conn (d/connect client {:db-name "test"}))
(def tx-data [[:examples/ensure-composite :db/ident :example/object2 :db/doc "Example object"]])
;; first ensure wins
(d/transact conn {:tx-data tx-data})
;; second ensure throws exception including t at which entity is known
(try
(d/transact conn {:tx-data tx-data})
(catch Exception e
(println "Got expected exception " (.getMessage e))
(def t (:t (ex-data (.getCause e))))))