This feature is in beta. Please try it out and provide feedback.
Datahike runs in ClojureScript on both Node.js and browser environments. The core query engine, pull API, and entity API work identically to Clojure. The main differences are in storage backends and async operation.
ClojureScript environments often require async I/O (IndexedDB, network). To support this, Datahike's persistent-sorted-set indices use partial continuation-passing style (partial-cps): storage operations return channels instead of values, while in-memory operations remain synchronous.
This is transparent when using datahike.api - async backends return channels
from create-database, connect, transact!, etc.
Node.js supports the file backend via konserve-node-filestore:
(ns my-app.core
(:require [datahike.api :as d]
[datahike.nodejs] ;; Registers :file backend
[cljs.core.async :refer [<!] :refer-macros [go]]))
(go
(let [config {:store {:backend :file
:path "./my-database"
:id #uuid "550e8400-e29b-41d4-a716-446655440000"}
:schema-flexibility :write}]
(<! (d/create-database config))
(let [conn (<! (d/connect config))]
(<! (d/transact! conn [{:name "Alice"}]))
(println (d/q '[:find ?n :where [?e :name ?n]] (d/db conn))))))
Browsers cannot use file storage directly. Datahike provides two browser-compatible backends:
Fast but not persistent across page reloads:
{:store {:backend :memory :id #uuid "550e8400-e29b-41d4-a716-446655440000"}}
Persistent browser storage via konserve-indexeddb:
{:store {:backend :indexeddb :id #uuid "550e8400-e29b-41d4-a716-446655440000"}}
IndexedDB is async, so all operations return channels.
For optimal performance, use TieredStore which combines a fast memory frontend with a persistent IndexedDB backend:
{:store {:backend :tiered
:id #uuid "550e8400-e29b-41d4-a716-446655440000"
:frontend-config {:backend :memory
:id #uuid "550e8400-e29b-41d4-a716-446655440000"}
:backend-config {:backend :indexeddb
:id #uuid "550e8400-e29b-41d4-a716-446655440000"}}}
Writes go to both stores (write-through). Reads come from memory. On reconnection, TieredStore syncs cached data from IndexedDB into memory before the sync handshake, so only keys newer than cached timestamps are transferred.
For browser applications that need a persistent backend, use the KabelWriter to connect to a JVM server. The server owns the database (using file storage) and streams updates to browser clients via WebSockets. See Streaming writer (Kabel) for setup instructions.
This architecture means:
For JavaScript/TypeScript applications, Datahike provides a Promise-based API. See JavaScript API for documentation.
Installation:
npm install datahike@next
Example:
const d = require('datahike');
const crypto = require('crypto');
const config = {
store: {
backend: ':memory',
id: crypto.randomUUID()
},
'schema-flexibility': ':read' // Allow schemaless data (use kebab-case)
};
await d.createDatabase(config);
const conn = await d.connect(config);
await d.transact(conn, [{ name: 'Alice' }]);
const db = await d.db(conn); // db() is async for async backends
const results = await d.q('[:find ?n :where [?e :name ?n]]', db);
console.log(results);
// => [['Alice']]
Can you improve this documentation?Edit on GitHub
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 |