npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

datahike

v0.8.1813

Published

Durable Datalog database for JavaScript and ClojureScript

Readme

Datahike - JavaScript API

Durable Datalog database for JavaScript and Node.js, powered by ClojureScript.

Features

  • Datalog Queries: Expressive query language inspired by Datomic
  • Schema Support: Optional schema with validation
  • Time Travel: Access database history and temporal queries
  • Pluggable Backends: Memory, file, or custom storage
  • Optional S3 Browser Backend: Direct access to S3-compatible buckets
  • Promise-based API: Native JavaScript async/await support
  • TypeScript Support: Complete type definitions included

Installation

npm install datahike

Quick Start

const d = require('datahike');

async function example() {
  // Create database configuration (requires UUID for :id)
  const config = {
    store: {
      backend: ':memory',
      id: d.randomUuid()
    },
    'value-caps': ':default'
  };

  // Create and connect to database
  await d.createDatabase(config);
  const conn = await d.connect(config);

  // Define schema
  // Keys: WITHOUT colon, Values: WITH colon
  const schema = [
    {
      'db/ident': ':name',
      'db/valueType': ':db.type/string',
      'db/cardinality': ':db.cardinality/one'
    },
    {
      'db/ident': ':age',
      'db/valueType': ':db.type/long',
      'db/cardinality': ':db.cardinality/one'
    }
  ];
  await d.transact(conn, schema);

  // Add data (data keys without colons)
  const data = [
    { name: 'Alice', age: 30 },
    { name: 'Bob', age: 25 }
  ];
  await d.transact(conn, data);

  // Query with Datalog
  const db = await d.db(conn);
  const results = await d.q(
    '[:find ?name ?age :where [?e :name ?name] [?e :age ?age]]',
    db
  );

  console.log(results); // [['Alice', 30], ['Bob', 25]]

  // Disconnect
  d.release(conn);
  await d.deleteDatabase(config);
}

example();

Datahike logs warnings and errors by default. Applications can change the runtime level, or disable library logging entirely:

d.setLogLevel('debug'); // 'off', 'trace', 'debug', 'info', 'warn', or 'error'

In Node.js, set DATAHIKE_LOG_LEVEL before importing the package to choose the initial level (for example, DATAHIKE_LOG_LEVEL=off node app.js).

Documentation

S3-compatible storage in browsers

Import the opt-in build when a browser should persist Datahike directly to Amazon S3, Cloudflare R2, MinIO, or another S3-compatible service:

import * as d from 'datahike/s3';

const storeId = d.randomUuid();
const config = {
  store: {
    backend: ':tiered',
    id: storeId,
    'frontend-config': { backend: ':memory', id: storeId },
    'backend-config': {
      backend: ':s3',
      endpoint: 'https://s3.us-west-1.amazonaws.com',
      bucket: 'my-datahike-bucket',
      region: 'us-west-1',
      'access-key': temporaryCredentials.accessKeyId,
      secret: temporaryCredentials.secretAccessKey,
      'session-token': temporaryCredentials.sessionToken,
      id: storeId
    }
  },
  writer: {
    backend: ':self',
    'writer-ownership': ':shared',
    'require-fencing': ':global'
  }
};

await d.createDatabase(config);
const conn = await d.connect(config);

The memory frontend is required: Datahike's query engine is synchronous, while browser S3 is asynchronous. Connection and shared-writer refreshes materialize the durable snapshot locally before returning it, after which q, pull, and the other read APIs remain synchronous over that immutable DB value. A bare :s3 store is deliberately unsupported for browser Datahike.

The bucket must provide strongly consistent object GET and LIST semantics, allow the browser origin through CORS, and expose the ETag response header. Use narrowly scoped, short-lived session credentials; never ship long-lived bucket credentials in browser code. :require-fencing :global makes a missing or unusable conditional-write guarantee a connection error instead of silently risking lost updates. Keep the store ID stable when reopening a database. The regular datahike entry does not include S3 code.

This direct-S3 mode is currently intended for small and medium databases. When another writer moves the head, the beta refresh path lists the durable store and copies only objects missing from the local tier; its remote request cost can therefore grow with the number of stored objects. Prefer a server-owned S3 store plus Kabel replication for large databases or sustained write contention.

Configuration

⚠️ Note: Keyword syntax may change in future versions to simplify the API.

const config = {
  store: {
    backend: ':memory',       // or ':file'
    id: d.randomUuid()        // Required: Datahike UUID identifier
  },
  // Optional configuration:
  'keep-history?': true,          // default: true
  'schema-flexibility': ':write'  // or ':read'
};

// File backend example (Node.js only)
const fileConfig = {
  store: {
    backend: ':file',
    path: './data'
  }
};

Keywords

Current keyword rules:

  • Schema keys: WITHOUT : prefix ('db/ident', not ':db/ident')
  • Schema values: WITH : prefix (':name', ':db.type/string')
  • Data keys: WITHOUT : prefix (name, age)
  • Pull patterns: WITH : prefix ([':name', ':age'])

Datalog Queries

Queries use EDN string format (Datalog DSL):

// Find relationships
await d.q('[:find ?e ?name :where [?e :name ?name]]', db);

// Find collection
await d.q('[:find [?name ...] :where [_ :name ?name]]', db);

// With predicates
await d.q('[:find ?name :where [?e :name ?name] [?e :age ?age] [(> ?age 25)]]', db);

// Parameterized
await d.q('[:find ?e :in $ ?name :where [?e :name ?name]]', db, 'Alice');

Pull API

Retrieve entity data by pattern:

// Pull single entity
await d.pull(db, ['*'], entityId);

// Pull with specific attributes
await d.pull(db, [':name', ':age'], entityId);

// Pull many entities
await d.pullMany(db, ['*'], [id1, id2, id3]);

Transactions

Add or retract data:

// Entity maps (data keys without colons)
const data = [
  { name: 'Charlie', age: 35 }
];
await d.transact(conn, data);

// Tuple form
await d.transact(conn, [
  [':db/add', entityId, ':age', 36]
]);

// Retract
await d.transact(conn, [
  [':db/retract', entityId, ':age', 35]
]);

Optimistic UI

Use an explicit overlay when a UI must show writes before the durable replica catches up:

const overlay = d.openOptimistic(conn);
const unsubscribe = d.optimisticListen(overlay, event => {
  render(event['db-after']);
});

const { result } = d.optimisticTransact(overlay, [
  [':db/add', entityId, ':age', 36]
]);
const outcome = await result; // { status: ':committed', ... } or ':rejected'

unsubscribe();
d.closeOptimistic(overlay);

Operation promises always resolve to tagged outcomes; a rejected transaction is not a rejected JavaScript Promise. Externally owned RPCs can use optimisticPredict, followed by optimisticAck or optimisticReject.

Temporal Queries

Access database history:

// Database at specific time
const currentDb = await d.db(conn);
const historicalDb = await d.asOf(currentDb, date);

// Full history
const historyDb = await d.history(currentDb);

Versioning and garbage collection

The JavaScript API exposes Datahike's commit graph and branch operations as Promises. Branch and merge parent collections are ordinary JavaScript arrays:

await d.branch(conn, ':db', ':feature');
const branchNames = await d.branches(conn); // [':db', ':feature']

const featureDb = await d.branchAsDb(conn, ':feature');
const commit = await d.commitId(featureDb); // UUID output is a string
const sameDb = await d.commitAsDb(conn, d.uuid(commit));

const report = await d.mergeDb(conn, [':feature'], [
  { name: 'merged value' }
]);
const parents = await d.parentCommitIds(report['db-after']);

await d.deleteBranch(conn, ':feature');

gcStorage reclaims unreachable objects from persistent stores and accepts a JavaScript Date or transaction time point. It returns an array of reclaimed store keys. It is not useful for a :memory store, whose index trees are kept inline rather than persisted as reclaimable objects.

const reclaimed = await d.gcStorage(conn, new Date(), {
  'min-age-ms': 60_000
});

With shared or remote writers, size min-age-ms above the longest possible values-before-head publication window plus clock skew. The default is 15 minutes for shared writers and zero for an exclusive local writer.

API Reference

See TypeScript definitions for complete API documentation.

Core Functions

  • createDatabase(config) - Create new database
  • deleteDatabase(config) - Delete database
  • databaseExists(config) - Check if database exists
  • connect(config) - Connect to database
  • release(conn) - Close connection
  • db(conn) - Get current database value
  • transact(conn, txData) - Execute transaction
  • q(query, ...args) - Execute Datalog query
  • pull(db, pattern, entityId) - Pull entity by pattern
  • pullMany(db, pattern, entityIds) - Pull multiple entities
  • entity(db, entityId) - Get entity (returns ClojureScript entity)
  • datoms(db, index, ...components) - Access datoms directly
  • seekDatoms(db, index, ...components) - Seek in index
  • schema(db) - Get database schema
  • reverse_schema(db) - Get reverse schema
  • metrics(db) - Get database metrics

Temporal Functions

  • asOf(db, timePoint) - Database at specific time
  • since(db, timePoint) - Changes since time
  • history(db) - Full database history

Known Limitations

  • Query API: Requires EDN string format (no JavaScript object syntax)
  • Entity API: Returns ClojureScript objects (use Pull API for plain JavaScript objects)
  • Keyword syntax: May change in future versions for simplification
  • Advanced Datalog: Some advanced features may have limited support

License

Eclipse Public License 1.0

Links