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

axiodb

v14.1.4

Published

The Pure JavaScript Alternative to SQLite. Embedded NoSQL database for Node.js with MongoDB-style queries, zero native dependencies, built-in InMemoryCache, and web GUI. Perfect for desktop apps, CLI tools, and embedded systems. No compilation, no platfor

Readme

AxioDB: The Pure JavaScript Alternative to SQLite

npm version npm downloads total npm downloads yearly npm downloads weekly npm downloads monthly License: MIT

Push to Registry CodeQL Socket Security

Node.js Version Tested on Node.js TypeScript Zero Dependencies

👉 Official Documentation: Access full guides, examples, and API references.


Table of Contents


What is AxioDB, and why does it exist?

AxioDB is an embedded NoSQL database for Node.js, with MongoDB-style queries, zero native dependencies, and a built-in web GUI. Think SQLite, but NoSQL — install it with npm, and you have a working database with no server, no compilation step, and no platform-specific binaries.

The problem

SQLite is great, but its native C bindings cause real deployment pain in JavaScript projects:

  • electron-rebuild on every Electron update
  • ❌ Platform-specific builds (Windows .node files ≠ Mac .node files)
  • ❌ SQL strings instead of JavaScript objects
  • ❌ Schema migrations when your data model changes
  • node-gyp compilation headaches

Meanwhile, plain JSON files have no querying, no caching, and no indexing — they just don't scale past a few thousand records. And MongoDB solves the query/caching problem, but needs a separate server process, which is overkill for a desktop app, CLI tool, or embedded system.

The solution

AxioDB combines the parts of each that actually matter for an embedded use case:

  • ✅ Works everywhere Node.js runs — no rebuild, no native dependencies
  • ✅ MongoDB-style queries: { age: { $gt: 25 } }
  • ✅ Schema-less JSON documents — no migrations
  • ✅ Built-in InMemoryCache with automatic invalidation
  • ✅ Multi-core parallelism with Worker Threads
  • ✅ Built-in web GUI at localhost:27018
  • ✅ AxioDBCloud — optional TCP remote access for Docker/cloud deployments

Is it a fit for you?

Great fit for:

  • 🖥️ Desktop apps (Electron, Tauri)
  • 🛠️ CLI tools
  • 📦 Embedded systems
  • 🚀 Rapid prototyping
  • 🏠 Local-first applications
  • 💻 Node.js apps requiring local storage

Sweet spot: 10K–500K documents with intelligent caching.

Not a fit for:

  • 10M+ documents, or datasets that need to scale far beyond a single node → use PostgreSQL, MongoDB, or SQLite
  • Multi-user web applications with hundreds of concurrent connections → AxioDB is single-instance, not a client-server database
  • Relational data with JOINs and foreign-key constraints → AxioDB is document-based NoSQL
  • Distributed systems needing replication, sharding, or clustering → AxioDB is single-node only
  • Cross-collection ACID transactions → AxioDB's transactions are scoped to a single collection

AxioDB isn't competing with PostgreSQL or MongoDB. It's for when you need a database embedded in your app — no server setup, no native dependencies. When you outgrow it, migrating to PostgreSQL or MongoDB is the right call, and expected.


📦 Installation

npm install axiodb@latest --save

Requirements: Node.js ≥20.0.0, npm ≥6.0.0 (yarn ≥1.0.0 optional). AxioDB runs on Node.js servers only — it requires the filesystem, so it does not run in a browser.


🛠️ Quick Start — Local AxioDB

const { AxioDB } = require('axiodb');

// Create AxioDB instance with the built-in GUI enabled
const db = new AxioDB({ GUI: true }); // GUI available at http://localhost:27018

// Create a database and a collection
const myDB = await db.createDB('HelloWorldDB');
const collection = await myDB.createCollection('greetings');

// Insert and query — Hello World! 👋
await collection.insert({ message: 'Hello, Developer! 👋' });
const result = await collection.query({}).exec();
console.log(result.data.documents[0].message); // Hello, Developer! 👋

Only one AxioDB instance per application. It's a singleton by design — create it once, then create as many databases and collections as you need under it.

new AxioDB(options?) — all constructor options

| Option | Type | Default | Description | | --- | --- | --- | --- | | GUI | boolean | false | Enable the web-based GUI dashboard at localhost:27018 | | RootName | string | "AxioDB" | Name of the root folder database files are stored under | | CustomPath | string | current working directory | Custom filesystem path for database storage | | TCP | boolean | false | Enable the AxioDBCloud TCP server on port 27019 | | TCPAuth | boolean | false | Require username/password authentication on TCP connections (same RBAC accounts as the GUI) — see Advanced: TCP authentication |

const db = new AxioDB({
  GUI: true,
  RootName: 'MyDB',
  CustomPath: './data',
  TCP: true,
  TCPAuth: true,
});

🚀 Features

Querying

  • Chainable Query API: .query(), .Sort(), .Limit(), .Skip(), .setCount(), .setProject(), .exec() / .findOne()
  • MongoDB-style Query Operators: $gt, $gte, $lt, $lte, $ne, $in, $nin, $exists, $regex, $or, $and
  • Aggregation Pipelines: MongoDB-compatible ($match, $group, $sort, $project, $limit, $skip, $unwind, $addFields, ...)
  • Bulk Operations: high-performance insertMany, UpdateMany, deleteMany

Indexing

  • Auto Indexing: every collection gets an automatic documentId index for O(1) lookups
  • Custom Field Indexes: newIndex(...fieldNames) to add fast lookups on any field, dropIndex(indexName) to remove one, getIndexes() to list what's registered
  • Index Cache with TTL: in-memory index cache with random 5–15 min TTL (prevents cache stampede) and disk persistence for cold-start recovery
  • Automatic Document Removal: documents are automatically removed from indexes when deleted
  • Dual-Write Pattern: indexes persist to both memory (speed) and disk (durability)

Transactions

  • ACID-compliant, single-collection transactions with savepoints, rollback, and Write-Ahead Logging (WAL) for crash recovery
  • Session Management: scoped transactions with timeout support
const session = collection.startSession();
await session.withTransaction(async (tx) => {
  await tx.insert({ name: 'Alice', balance: 1000 });
  await tx.update({ name: 'Bob' }, { $inc: { balance: -100 } });
  // Auto-commits on success, auto-rolls-back on error
});

Caching

  • InMemoryCache: automatic eviction policies, random TTL (5–15 min) to avoid thundering-herd cache expiry
  • Selective Invalidation: only the affected cache entries are cleared on update/delete — not the whole cache
  • Async, Non-blocking Updates: cache writes don't block the response path
  • Collection-Scoped Keys: cache keys include the collection path, so there's no cross-collection collision

Security

Architecture

  • Tree-like Storage: hierarchical, file-per-document layout for efficient retrieval, selective loading, and easy backup
  • Worker Threads: non-blocking I/O and multi-core utilization, especially for reads
  • Single Instance Architecture: one AxioDB instance manages unlimited databases and collections, with strong consistency
  • Zero-Configuration Setup: serverless — install and start building instantly
  • Custom Database Path: flexible storage location via CustomPath

GUI & Remote Access

  • Web-based GUI Dashboard: visual database browser, query execution, real-time monitoring at localhost:27018
  • Role-Based Access Control: Super Admin / Admin / View roles, shared between the GUI and AxioDBCloud
  • AxioDBCloud: TCP-based remote access — connect to a running AxioDB instance from anywhere with the exact same API as embedded mode

☁️ AxioDBCloud — Connecting Remotely

Host AxioDB in Docker or on a server, connect from anywhere — AxioDBCloud is a TCP client that mirrors the embedded API exactly, so switching from local to remote is a one-line change (new AxioDB()new AxioDBCloud()).

  • 🔄 Zero Code Changes: same createDB/createCollection/insert/query API as embedded AxioDB
  • ⚡ Fast Binary Protocol: length-prefixed JSON framing, with automatic reconnection
  • 🔐 Optional Authentication: shared RBAC with the GUI, per-IP rate limiting (see Advanced below)
  • 📦 35+ Commands: full CRUD, aggregation, and indexing over the wire
  • 🔁 Auto-Reconnect: exponential backoff, up to 10 retry attempts
  • 💓 Heartbeat Monitoring: PING/PONG every 30 seconds
  • 🆔 Request Correlation: UUID-based request/response matching
  • 🧵 Connection Pooling: client keeps a pool of maxPoolSize concurrent connections (default: 10, mirrors MongoDB's driver option) and routes each command to the least-busy connected member (fewest in-flight requests); server accepts 1,000+ concurrent connections total, capped at 100 per remote IP (see the file descriptor limit note below if you're running near that scale)
  • 🛡️ Connection-Level DoS Protection: per-IP concurrent connection cap (100) plus a separate per-IP connection-attempt rate limiter (300 attempts / 10s → 30s cooldown), so one client can't starve the server either by holding too many sockets open or by rapidly opening and dropping them
  • 🔒 Optional TLS Encryption: encrypt the wire protocol with your own cert (see Advanced: TLS below) — off by default, so existing plaintext deployments are unaffected unless you turn it on
  • 📐 TypeScript Support: full type definitions included

Use cases: microservices sharing one AxioDB instance, Electron apps connecting to a local or remote database, teams sharing a development database, container/cloud deployments (AWS, Azure, GCP, DigitalOcean).

Simple: connect without authentication

By default, TCP connections are unauthenticated — anyone who can reach the port can run any command. This is fine for local development or a fully trusted private network.

Server:

const { AxioDB } = require('axiodb');
const db = new AxioDB({ GUI: false, RootName: 'MyDB', CustomPath: '.', TCP: true }); // TCP on port 27019

Client:

const { AxioDBCloud } = require('axiodb');

const client = new AxioDBCloud("axiodb://localhost:27019");
await client.connect();

const db = await client.createDB("ProductionDB");
const users = await db.createCollection("Users");

await users.insert({ name: "Alice", role: "admin" });
const results = await users.query({ role: "admin" })
  .Limit(10)
  .Sort({ createdAt: -1 })
  .exec();

await client.disconnect();

Advanced: TCP authentication

Opt in with TCPAuth: true to require a username/password on every connection. This reuses the exact same accounts and roles as the GUI's RBAC system (see Built-in Web GUI & Authentication) — one set of credentials for both.

Server:

const db = new AxioDB({ TCP: true, TCPAuth: true, RootName: 'MyDB', CustomPath: '.' });

Client — credentials in the constructor (recommended; connect() authenticates automatically):

const client = new AxioDBCloud("axiodb://localhost:27019", {
  username: 'admin',
  password: 'admin',
});
await client.connect();

console.log(client.authenticatedUser); // { username, role, mustChangePassword }

Client — authenticate after connecting (e.g. credentials supplied at runtime):

const client = new AxioDBCloud("axiodb://localhost:27019");
await client.connect();
await client.login('admin', 'admin');

What's enforced:

  • Every command except PING/DISCONNECT/AUTHENTICATE requires a prior successful login on that connection.
  • The same role permissions as the GUI apply per command (e.g. a View-role user gets 403 on CREATE_DB).
  • Shared per-IP login rate limiter with the GUI: 5 failed attempts within a trailing 15-minute window locks that IP out for 15 minutes (429 Too Many Requests) — counted across both TCP and GUI login attempts from that IP.
  • Accounts that still need their forced password change are rejected outright (403), not allowed through with a warning — there's no TCP command to change a password today, so log into the GUI (http://localhost:27018) to complete it first, or authenticate with an account that already has.
  • If a Super Admin resets a user's password, changes their role, or deletes them via the GUI while that user has an open TCP connection, the TCP connection is immediately forced to re-authenticate on its next command.

Known limitations: there's currently no TCP command to change a password; that must go through the GUI.

Advanced: TLS encryption

By default, the TCP protocol is plaintext — anyone who can capture the network traffic between client and server (e.g. Wireshark on a shared network) can read your data and, if TCPAuth is on, your password. TLS fixes this. It's off by default — nothing below is required, and existing plaintext deployments keep working exactly as before unless you turn it on.

You must provide your own certificate + key. AxioDB never generates one for you — that's a security decision only you can make (a real cert from a CA, or a self-signed one for local/private use).

Step 1 — get a cert + key. For local/dev/private use, generate a self-signed one (one-time, takes a second):

openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes -subj "/CN=localhost"

This creates two files, cert.pem and key.pem, in your current folder. For a real production deployment reachable from the internet, use a cert from a real CA (Let's Encrypt, your org's CA, your cloud provider's managed cert) instead — the rest of the setup below is identical either way.

Step 2 — point the server at them:

const { AxioDB } = require('axiodb');
const db = new AxioDB({
  TCP: true,
  TLS: true,
  TLSCertPath: './cert.pem', // path to the file from step 1
  TLSKeyPath: './key.pem',
});

If TLS: true but either path is missing or unreadable, AxioDB throws immediately at startup — it never silently falls back to plaintext.

Step 3 — point the client at the same cert (only needed because it's self-signed; a real CA-issued cert wouldn't need this step, the same way your browser trusts https:// sites without extra setup):

const { AxioDBCloud } = require('axiodb');
const client = new AxioDBCloud("axiodb://localhost:27019", {
  tls: true,
  tlsCAPath: './cert.pem', // same cert.pem from step 1 - proves this server is the real one
});
await client.connect();

Without tlsCAPath, the client refuses to connect to a self-signed server by default (tlsRejectUnauthorized defaults to true) — this is intentional, it's the same protection that stops your browser from silently trusting a fake https:// site. Only set tlsRejectUnauthorized: false for local/dev testing, never in production, since it turns that protection off entirely.

Running this in Docker? The cert/key files need to get into the container. The simplest way to think about it: your cert files live on your real machine; a Docker bind mount (-v) makes a folder from your machine visible inside the container at whatever path you choose, and you point AXIODB_TLS_CERT_PATH/AXIODB_TLS_KEY_PATH at that in-container path, not your real machine's path:

# cert.pem and key.pem are really at /home/you/mycerts/ on your machine.
# "/certs" below is just a name we're choosing for where they'll appear inside the container.
docker run -d --name axiodb-server \
  -p 27018:27018 -p 27019:27019 \
  -v /home/you/mycerts:/certs:ro \
  -e AXIODB_TLS=true \
  -e AXIODB_TLS_CERT_PATH=/certs/cert.pem \
  -e AXIODB_TLS_KEY_PATH=/certs/key.pem \
  theankansaha/axiodb

The rule: the -e AXIODB_TLS_CERT_PATH=... value must always match the right-hand side of the -v mount (/certs/...), never the real path on your machine (/home/you/mycerts/...) — the container can't see your machine's filesystem directly, only whatever you've explicitly mounted into it.

👉 Full AxioDBCloud Documentation — setup guides, API reference, Docker examples


🔧 Troubleshooting

"Not connected to server" right after calling connect()

client.connect() is asynchronous and must be awaited before you use the connection — it resolves only once the TCP handshake (and, if TCPAuth is on, the AUTHENTICATE round-trip) has completed.

// ❌ Wrong — races ahead before the connection (and login) finish
client.connect();
console.log(client.authenticatedUser); // undefined
await client.createDB("MyDB");         // "Not connected to server"

// ✅ Right
await client.connect();
console.log(client.authenticatedUser); // populated
await client.createDB("MyDB");         // works

401 — "Authentication required..."

You're running with TCPAuth: true and sent a command before a successful AUTHENTICATE. Either pass { username, password } in the AxioDBCloud constructor (auto-authenticates on connect()), or call await client.login(username, password) yourself before any other command.

403 — "This account must change its password before it can be used over TCP..."

Your credentials are correct, but that account is still flagged for a forced password change (true for the default admin/admin account, and for any newly created user). Log into the GUI at http://localhost:27018, sign in, and complete the password change there — there's no TCP command for this yet. Then reconnect with the new password, or use a different account that has already completed its change.

429 — "Too many failed login attempts..."

Five failed logins from your IP within 15 minutes trigger a 15-minute lockout, shared between TCP and the GUI. Double check the credentials you're sending, wait out the cooldown, or fix the underlying typo/config issue causing repeated failures — there's no way to clear the lockout early.

429 — "Too many concurrent connections from this IP address"

Unrelated to the login lockout above — this fires at connection time, before any AUTHENTICATE, once a single remote IP has 100 concurrent open TCP sockets to the server (MAX_CONNECTIONS_PER_IP), regardless of whether any of those connections are authenticated. This caps how much of the server's total 1,000-connection budget one IP can claim, so one client can't starve every other client. It's per-connection, not per-request — a single AxioDBCloud client at the default maxPoolSize: 10 is nowhere near this limit; you'd only hit it by running many separate client processes behind the same IP/NAT gateway, or a runaway reconnect loop leaking sockets. If you legitimately need more than 100 concurrent connections from one IP, that's a server-side constant (MAX_CONNECTIONS_PER_IP in source/tcp/config/keys.ts) — there's no runtime option to raise it yet.

If you do set a maxPoolSize that pushes past this cap (or any other subset of the pool fails for another reason, e.g. a network blip), connect() doesn't throw as long as at least one pool member connected — it resolves with a smaller-than-requested pool and emits a poolDegraded event so you know about it instead of silently running under capacity:

client.on('poolDegraded', ({ requested, connected, failed, errors }) => {
  console.warn(`Pool came up smaller than requested: ${connected}/${requested} connected, ${failed} failed`);
  console.warn(errors[0].message); // e.g. "Too many concurrent connections from this IP address"
});

await client.connect(); // resolves even if some pool members were rejected

(The very first connection in the pool is the exception — if that one fails, connect() rejects entirely, since it's the signal that the server is reachable and credentials are valid at all.)

429 — "Too many connection attempts from this IP address. Try again later."

Different from both 429s above, and checked first: this guards against rapid connect-then-drop churn, which the concurrent-connection cap (MAX_CONNECTIONS_PER_IP) doesn't catch on its own — an attacker who never holds more than a few sockets open at once could still hammer the server with a high rate of connection attempts, each costing a TCP handshake and an accept/reject cycle, and stay under that cap the whole time. This is tracked separately, per IP, as a sliding window: once an IP crosses 300 connection attempts (successful or rejected, it doesn't matter which) within a trailing 10-second window, every new connection from that IP is rejected outright for the next 30 seconds. A normal AxioDBCloud client, even reconnecting a full maxPoolSize: 10 pool repeatedly, is nowhere near this threshold — you'd only hit it via a genuine connection flood or a reconnect loop gone very wrong (e.g. retrying without backoff). There's no runtime option to raise these thresholds yet; they're constants in source/tcp/config/keys.ts (CONNECTION_RATE_LIMIT_*).

403 — "This is a reserved system database"

You (or a client) tried to read/write a database literally named config — that name is reserved for AxioDB's own RBAC storage (users/roles/permissions) and is blocked on both the GUI and TCP, authenticated or not. Use a different database name.

Connection refused / timeout connecting to axiodb://host:27019

  • Confirm the server was started with TCP: true (or, in Docker, AXIODB_TCP=true, the default).
  • Confirm the port is published: -p 27019:27019 on docker run, or that nothing else on the host is bound to 27019.
  • If you're getting a protocol error mentioning "Message exceeds maximum size" or "Received HTTP data on TCP port," you're likely pointed at the GUI port (27018) instead of the TCP port (27019) — check your connection string.

Docker container issues (won't start, port conflicts, data not persisting)

See the Docker Deployment section below, and Docker/README.md in the repository for a fuller Docker-specific troubleshooting guide (docker logs, port-conflict remapping, volume-mounting checklist).

Connection refused / "Too many open files" errors at high concurrency

Each AxioDBCloud client opens maxPoolSize TCP sockets (default: 10), and the server holds one socket per connected client — both count against the OS's open-file-descriptor limit. Most Linux distros default ulimit -n to 1024 per process, which is enough for only ~100 clients at the default pool size before the server starts refusing new connections with EMFILE/ENFILE.

The published Docker image already raises its own soft limit to 65536 on startup (see Docker/Dockerfile's CMD), so this is normally only something to think about on bare-metal/host installs, or if your Docker host's hard limit is itself capped below 65536:

ulimit -n 65536          # current shell / process (non-Docker deployments)
node lib/config/DB.js

If the container still refuses connections, the host's hard limit is the ceiling to raise instead:

docker run --ulimit nofile=65536:65536 -p 27018:27018 -p 27019:27019 theankansaha/axiodb

or, in Compose:

services:
  axiodb:
    image: theankansaha/axiodb
    ulimits:
      nofile:
        soft: 65536
        hard: 65536

On the client side, prefer a smaller maxPoolSize per instance rather than raising it — the least-busy routing (see Connection Pooling above) already avoids the head-of-line blocking a bigger pool would otherwise compensate for.

Raising libuv's thread pool for higher disk-I/O concurrency

File reads/writes (FileManager) go through Node's async fs APIs, which run on libuv's threadpool - 4 threads by default, regardless of how many TCP connections are open. Under many concurrent clients doing real disk I/O at once, that pool - not connection count - is the throughput ceiling.

The published image (Docker/runner.js) computes a default automatically from the container's actual CPU allotment (its cgroup quota, not the host's core count - --cpus/Kubernetes resources.limits.cpu are read directly, since Node has no stdlib API for this), roughly 4 × allotted CPUs, clamped to [4, 64]. Override it explicitly if you want a fixed value instead, no rebuild required:

docker run -e UV_THREADPOOL_SIZE=16 -p 27018:27018 -p 27019:27019 theankansaha/axiodb

🐳 Docker Deployment

Simple: run the container

docker run -d \
  --name axiodb-server \
  -p 27018:27018 \
  -p 27019:27019 \
  -e AXIODB_TCP_AUTH=true \
  -v axiodb-data:/app \
  theankansaha/axiodb

# Ports:
# 27018 - HTTP GUI Dashboard
# 27019 - TCP Remote Access (AxioDBCloud)
# Volume: /app is the main data directory

TCP authentication is on by default in the image. Log into the GUI at http://localhost:27018 as admin/admin to complete the forced password change before connecting over TCP (see Troubleshooting if you skip this step).

Advanced: env vars, volumes, Compose

Every option below has a default matching the image's previous fixed behavior — override any of them with -e VAR=value at docker run time, no rebuild required:

| Variable | Default | Description | | --- | --- | --- | | AXIODB_GUI | true | Enable the HTTP Control Server / web GUI on port 27018 | | AXIODB_TCP | true | Enable the AxioDBCloud TCP server on port 27019 | | AXIODB_TCP_AUTH | true | Require username/password authentication on TCP connections (same RBAC accounts as the GUI) | | AXIODB_TLS | false | Encrypt TCP connections with TLS instead of plaintext (see Advanced: TLS encryption) | | AXIODB_TLS_CERT_PATH | (none) | Path inside the container to a PEM cert file - required when AXIODB_TLS=true. Mount the real file in with -v first (see the TLS section above) | | AXIODB_TLS_KEY_PATH | (none) | Path inside the container to the matching PEM private key - required when AXIODB_TLS=true | | AXIODB_ROOT_NAME | AxioDB | Name of the root database folder created under the data volume | | AXIODB_CUSTOM_PATH | (container's working directory) | Custom path for database storage inside the container | | AXIODB_MCP | false | Enable the MCP server (AI agent integration) on port 27020 - see MCP Server | | AXIODB_MCP_PORT | 27020 | Port the MCP server listens on inside the container |

Ports themselves (27018/27019) aren't configurable via environment variable — remap them at the Docker layer with -p <host-port>:27018 / -p <host-port>:27019.

Disabling TCP authentication (only on a trusted private network — the wire is plaintext unless you also enable AXIODB_TLS; see Advanced: TLS encryption):

docker run -d \
  --name axiodb-server \
  -p 27018:27018 \
  -p 27019:27019 \
  -e AXIODB_TCP_AUTH=false \
  -v axiodb-data:/app \
  theankansaha/axiodb

Docker Compose:

version: "3.8"

services:
  axiodb:
    image: theankansaha/axiodb
    container_name: axiodb-server
    ports:
      - "27018:27018"
      - "27019:27019"
    environment:
      - AXIODB_GUI=true
      - AXIODB_TCP=true
      - AXIODB_TCP_AUTH=true
      - AXIODB_ROOT_NAME=AxioDB
    volumes:
      - axiodb-data:/app
    restart: unless-stopped

volumes:
  axiodb-data:

The same, with TLS enabled — note the two different kinds of entry under volumes:: ./mycerts:/certs:ro is your real folder on the machine running Compose (because it contains a /), mounted read-only at /certs inside the container; axiodb-data:/app is a Docker-managed named volume (no /, just a label) for the actual database files:

version: "3.8"

services:
  axiodb:
    image: theankansaha/axiodb
    container_name: axiodb-server
    ports:
      - "27018:27018"
      - "27019:27019"
    environment:
      - AXIODB_GUI=true
      - AXIODB_TCP=true
      - AXIODB_TCP_AUTH=true
      - AXIODB_TLS=true
      - AXIODB_TLS_CERT_PATH=/certs/cert.pem
      - AXIODB_TLS_KEY_PATH=/certs/key.pem
      - AXIODB_ROOT_NAME=AxioDB
    volumes:
      - ./mycerts:/certs:ro      # your real cert.pem/key.pem folder -> /certs in the container
      - axiodb-data:/app         # Docker-managed volume for database files
    restart: unless-stopped

volumes:
  axiodb-data:

Building the image from source, and a fuller Docker troubleshooting guide (container won't start, port-in-use, data-persistence checks) live in Docker/README.md — the canonical Docker doc, not duplicated here in full.


🤖 MCP Server — AI Agent Integration

Spin up the same Docker container with AXIODB_MCP=true and let Claude (or any MCP-compatible AI agent) talk to your AxioDB instance directly — 32 tools covering databases, collections, documents, aggregation, indexes, dashboard stats, and user/role management, all gated by the same RBAC as the web GUI. It runs in the same process as your existing container; nothing new to install, no second database instance.

docker run -d \
  --name axiodb-server \
  -e AXIODB_GUI=true \
  -e AXIODB_MCP=true \
  -p 27018:27018 \
  -p 27019:27019 \
  -p 27020:27020 \
  -v axiodb-data:/app \
  theankansaha/axiodb

Register the endpoint (http://localhost:27020/mcp) with whichever AI tool you use:

| Tool | How | | --- | --- | | Claude Code | claude mcp add --transport http axiodb http://localhost:27020/mcp | | OpenAI Codex CLI | codex mcp add axiodb --url http://localhost:27020/mcp (or [mcp_servers.axiodb] + url = "..." in ~/.codex/config.toml) | | opencode | opencode mcp add (interactive → type "remote") or add "axiodb": { "type": "remote", "url": "...", "enabled": true } under mcp in opencode.json | | GitHub Copilot CLI | /mcp add inside the copilot REPL, or add to ~/.copilot/mcp-config.json: { "mcpServers": { "axiodb": { "type": "http", "url": "..." } } } | | Cursor | Add to .cursor/mcp.json (or ~/.cursor/mcp.json): { "mcpServers": { "axiodb": { "url": "..." } } } | | Windsurf | Add to ~/.codeium/windsurf/mcp_config.json: { "mcpServers": { "axiodb": { "serverUrl": "..." } } } | | Google Antigravity (IDE & CLI) | Add to ~/.gemini/config/mcp_config.json: { "mcpServers": { "axiodb": { "serverUrl": "..." } } } — note serverUrl, not url |

Every tool except axiodb_login requires a sessionId obtained by logging in first (default seeded account: admin/admin, same as the GUI) — every subsequent call is checked against that logged-in user's actual role, exactly like the HTTP Control Server. A View-role session gets a real 403 on write tools; nothing is gated by a static container environment variable.

AXIODB_MCP=true only has RBAC to serve once it's actually seeded, which requires AXIODB_GUI=true (the default) or AXIODB_TCP=true + AXIODB_TCP_AUTH=true.

Full tool catalogue, examples, and security notes: MCP Server docs.


🎨 Built-in Web GUI & Authentication (RBAC)

AxioDB includes a built-in web-based GUI for database visualization and management — perfect for Electron apps and development environments.

Enabling the GUI

// Enable GUI when creating the AxioDB instance
const db = new AxioDB({ GUI: true }); // GUI available at localhost:27018

// With a custom database path
const db = new AxioDB({ GUI: true, RootName: "MyDB", CustomPath: "./custom/path" });

GUI Features: visual database and collection browser, real-time data inspection, query execution interface, performance monitoring, no external dependencies required. Access at http://localhost:27018 when enabled.

Authentication & Access Control

The Control Server ships with built-in login and role-based access control (RBAC) — the same system TCP's TCPAuth reuses. On first start with GUI: true (or TCP: true, TCPAuth: true), AxioDB seeds a reserved config database (hidden from the regular database list) containing three collections — users, roles, permissions — and a default account:

Username: admin
Password: admin

You'll be forced to change this password on first login (this applies to every account, not just the default one — there's currently no way around it other than completing the change via the GUI). Three predefined roles are seeded automatically:

| Role | Access | |------|--------| | Super Admin | Full access, including creating users/roles | | Admin | Full database/collection/document access, no user or role management | | View | Read-only access to databases, collections, documents, and indexes |

A Super Admin can create additional roles from the predefined permission catalogue and create new users with any role. Sessions are held only in server memory (never persisted to disk) and are tied to an httpOnly cookie, so restarting the server logs everyone out.

Login rate limiting: after 5 failed login attempts from the same IP within a trailing 15-minute window, that IP is locked out for 15 minutes (429 Too Many Requests) — regardless of username. This limiter is shared with TCP AUTHENTICATE attempts (see Troubleshooting for what the error looks like).

Index management: the Control Server also exposes GET /api/index/list, POST /api/index/create, and DELETE /api/index/delete, gated by the same index:view / index:create / index:delete permissions (View role gets view-only, Admin and Super Admin get all three).

Security note: RBAC protects the Control Server's HTTP API and TCP server, but the HTTP GUI itself has no TLS support - keep it on a trusted local/private network, not public internet exposure. The TCP server can be encrypted (see Advanced: TLS encryption), which is recommended if it's reachable over any untrusted network.


🛠️ Detailed Usage

Collection Creation Options

createCollection(
  name: string,           // Name of the collection (required)
)

Example

const { AxioDB } = require("axiodb");
const db = new AxioDB();

const userDB = await db.createDB("MyDB");

// Create a collection
const userCollection = await userDB.createCollection("Users");

await userCollection.insert({
  name: "John Doe",
  email: "[email protected]",
  age: 30,
});

const results = await userCollection
  .query({ age: { $gt: 25 } })
  .Limit(10)
  .Sort({ age: 1 })
  .exec();
console.log(results.data.documents);

Worked example: e-commerce product catalog

const { AxioDB } = require('axiodb');
const db = new AxioDB();

const shopDB = await db.createDB('ecommerce');
const products = await shopDB.createCollection('products');

await products.insert({
  name: 'Laptop',
  price: 999.99,
  category: 'Electronics',
  inStock: true,
});

// Sorted, filtered query
const electronics = await products
  .query({ category: 'Electronics', inStock: true })
  .Sort({ price: 1 })
  .exec();

🌟 Advanced Features

  • Multiple Databases: architect scalable apps with multiple databases and collections, each with independent security settings
  • Custom Query Processing: the full operator set ($gt, $lt, $in, $regex, $gte, $lte, $ne, $nin, $exists, $or, $and) plus aggregation pipelines
  • Enterprise Data Management: bulk operations, conditional updates, atomic transactions
  • Performance Optimization: fast lookups, pagination, and intelligent caching with random TTL

📖 API Reference

AxioDB

  • createDB(dbName: string): Promise<Database>
  • deleteDatabase(dbName: string): Promise<SuccessInterface | ErrorInterface>

Database

  • createCollection(name: string): Promise<Collection>
  • deleteCollection(name: string): Promise<SuccessInterface | ErrorInterface>
  • getCollectionInfo(): Promise<SuccessInterface>

Collection

  • insert(document: object): Promise<SuccessInterface | ErrorInterface>
  • insertMany(documents: object[]): Promise<SuccessInterface | ErrorInterface>
  • query(query: object): Reader
  • update(query: object): Updater
  • delete(query: object): Deleter
  • aggregate(pipeline: object[]): Aggregation
  • startSession(options?: SessionOptions): Session
  • newIndex(...fieldNames: string[]): Promise<SuccessInterface>
  • dropIndex(indexName: string): Promise<SuccessInterface | ErrorInterface>
  • getIndexes(): Promise<SuccessInterface | ErrorInterface> — lists all indexes registered on the collection

Updater / Deleter

update(query) and delete(query) on their own don't change anything — they return a chainable object. Call one of the methods below to actually apply the change:

  • updater.UpdateOne(data: object): Promise<SuccessInterface | ErrorInterface> — applies data to the first document matching query
  • updater.UpdateMany(data: object): Promise<SuccessInterface | ErrorInterface> — applies data to every document matching query
  • deleter.deleteOne(): Promise<SuccessInterface | ErrorInterface> — deletes the first document matching query
  • deleter.deleteMany(): Promise<SuccessInterface | ErrorInterface> — deletes every document matching query
// Update the first matching document
await collection.update({ name: 'Alice' }).UpdateOne({ status: 'active' });

// Update every matching document
await collection.update({ role: 'trial' }).UpdateMany({ role: 'active' });

// Delete the first matching document
await collection.delete({ name: 'Alice' }).deleteOne();

// Delete every matching document
await collection.delete({ status: 'inactive' }).deleteMany();

Reader

  • Limit(limit: number): Reader
  • Skip(skip: number): Reader
  • Sort(sort: object): Reader
  • setCount(count: boolean): Reader
  • setProject(project: object): Reader
  • exec(): Promise<SuccessInterface | ErrorInterface>
  • findOne(): Promise<SuccessInterface | ErrorInterface>

Transaction (Session)

  • startSession(options?: { timeout?: number }): Session
  • session.withTransaction(callback: Function): Promise<any>
  • session.startTransaction(): Transaction
  • transaction.insert(document: object): Transaction
  • transaction.update(query: object, update: object): Transaction
  • transaction.delete(query: object): Transaction
  • transaction.savepoint(name: string): Transaction
  • transaction.rollbackToSavepoint(name: string): Transaction
  • transaction.commit(): Promise<SuccessInterface>
  • transaction.rollback(): Promise<void>

✅ Best Practices

Use environment variables for TCP credentials — never hardcode them:

// ❌ Bad
const client = new AxioDBCloud("axiodb://localhost:27019", {
  username: 'admin',
  password: 'myPassword123',
});

// ✅ Good
const client = new AxioDBCloud("axiodb://localhost:27019", {
  username: process.env.AXIODB_TCP_USERNAME,
  password: process.env.AXIODB_TCP_PASSWORD,
});

Use documentId for the fastest possible lookups — it's the one field that's always indexed automatically, backed by InMemoryCache:

const user = await collection.query({ documentId: 'ABC123' }).exec();

Handle errors explicitly — AxioDB operations reject/return error responses rather than throwing silently:

try {
  await collection.insert({ name: 'User' });
} catch (error) {
  console.error('Insert failed:', error);
}

Clean up resources you no longer need:

await database.deleteCollection('tempCollection');
await db.deleteDatabase('tempDB');

Access control:

  • Never hardcode credentials — use environment variables or a secrets manager
  • Implement proper access controls and take regular backups
  • For AxioDBCloud/GUI, rotate the default admin password immediately (see Authentication & Access Control)

For vulnerability reporting, see SECURITY.md.


⚙️ Architecture & Internal Mechanisms

  • Tree Structure for Fast Data Retrieval: hierarchical storage enables O(1) document lookups and efficient indexing. Each document is isolated in its own file, supporting selective loading and easy backup.
  • Worker Threads for Parallel Processing: leverages Node.js Worker Threads for non-blocking I/O, multi-core utilization, and scalable performance — especially for read operations.
  • InMemoryCache System: automatic eviction policies, TTL support, and memory optimization, delivering sub-millisecond response times for frequently accessed data.
  • Query Processing Pipeline: intelligent caching, parallelized processing, lazy evaluation, and just-in-time query optimization.
  • Single Instance Architecture: ensures ACID compliance, strong data consistency, and simplified deployment — one AxioDB instance manages all databases and collections.
  • Designed for Node.js Developers: native JavaScript API, promise-based interface, lightweight dependency footprint, simple learning curve.

🏆 Comparisons

AxioDB vs SQLite

| Feature | SQLite | AxioDB | | ------- | ------ | ------ | | Native Dependencies | ❌ Yes (C bindings) | ✅ Pure JavaScript | | Query Language | SQL Strings | JavaScript Objects | | Schema Migrations | ❌ Required (ALTER TABLE) | ✅ Schema-less | | Built-in Caching | ⚠️ Manual | ✅ InMemoryCache | | Multi-core Processing | ❌ Single-threaded | ✅ Worker Threads | | Built-in GUI | ❌ External tools only | ✅ Web interface included | | Best For | 10M+ records, relational data | 10K–500K documents, embedded apps |

AxioDB vs Traditional JSON Files

| Feature | Traditional JSON Files | AxioDB | | ------- | --------------------- | ------ | | Storage | Single JSON file | File-per-document | | Caching | None | InMemoryCache | | Indexing | None | Auto documentId + custom fields | | Query Speed | Linear O(n) | Sub-millisecond O(1) | | Scalability | Poor | Excellent (up to sweet spot) | | Built-in Query Operators | None | $gt, $lt, $regex, $in, ... |

Benchmark: AxioDB's documentId search with InMemoryCache provides instant retrieval compared to traditional JSON files, which require full-file parsing (tested with 1M+ documents).

AxioDB vs lowdb, nedb, better-sqlite3

| Feature | lowdb | nedb | better-sqlite3 | AxioDB | |---------|-------|------|---------------|--------| | Maintained | ✅ | ❌ Abandoned | ✅ | ✅ | | Native bindings | ✅ None | ✅ None | ❌ Yes (C/node-gyp) | ✅ None | | Storage | Single JSON file | Single file / in-memory | Single .db file | File-per-document | | Query language | JS/Lodash | JS objects | SQL strings | JS objects (MongoDB-style) | | Built-in caching | ❌ | ❌ | ❌ | ✅ InMemoryCache | | Worker Threads | ❌ | ❌ | ❌ | ✅ | | ACID Transactions | ❌ | ❌ | ✅ | ✅ | | Aggregation Pipelines | ❌ | Partial | ❌ | ✅ MongoDB-compatible | | TypeScript support | ✅ | Partial | ✅ | ✅ Full | | Electron compatible | ✅ | ✅ | ❌ (requires rebuild) | ✅ | | Sweet spot | <5K docs | <100K docs | 10M+ (relational) | 10K–500K docs | | Built-in GUI | ❌ | ❌ | ❌ | ✅ localhost:27018 |


⚠️ Limitations & Honest Positioning

  • Dataset Size: optimized for 10K–500K documents. For 10M+, use PostgreSQL, MongoDB, or SQLite.
  • Concurrency: single-instance architecture. For multi-user web apps with hundreds of concurrent connections, use a traditional client-server database.
  • Relational Data: document-based NoSQL, no JOIN operations. For complex relational data with foreign keys, use a SQL database.
  • Distributed Systems: single-node only — no replication, sharding, or clustering. Use MongoDB or CouchDB for that.
  • Transactions: single-collection ACID transactions only. For cross-collection transaction requirements, use PostgreSQL or MongoDB.

None of this is a shortcoming to apologize for — AxioDB is deliberately scoped to the embedded/local-first niche. When you outgrow it, that's a sign to migrate, not a bug to file.


❓ FAQ

Q: What is AxioDB? An embedded NoSQL database for Node.js. Pure JavaScript, zero native dependencies. npm install axiodb and you have a database — no server, no node-gyp, no electron-rebuild.

Q: Is AxioDB a replacement for MongoDB? No. AxioDB is embedded (runs inside your app); MongoDB is a client-server database for multi-user systems. Use AxioDB for desktop apps, CLI tools, and local-first apps up to ~500K documents; use MongoDB when you need a shared networked database.

Q: Does AxioDB work with Electron? Yes — this is the primary use case it was built for. Zero native dependencies means no electron-rebuild, no platform-specific .node files, no compilation step.

Q: How does AxioDB compare to better-sqlite3 / lowdb / nedb? See the Comparisons tables above for the full breakdown — in short: no native bindings (unlike better-sqlite3), no single-file bottleneck (unlike lowdb), and actively maintained with TypeScript/transactions (unlike the abandoned nedb).

Q: How many documents can AxioDB handle? Optimized for 10,000–500,000 documents. For 1M+, use PostgreSQL or MongoDB. documentId lookups take ~1ms on 10K documents with InMemoryCache.

Q: Does AxioDB support TypeScript? Yes. Full type definitions are included — no separate @types package needed.

Q: Does AxioDB work in the browser? No. AxioDB requires Node.js (v20+) and the filesystem — server-side and desktop only.

Q: What is AxioDBCloud? TCP-based remote access for AxioDB. Deploy AxioDB in Docker, connect from multiple clients with the exact same API. Supports 1,000+ concurrent connections with auto-reconnect. Optional username/password authentication (TCPAuth: true) reuses the same RBAC accounts as the GUI, and optional TLS encryption (TLS: true) protects the wire protocol on untrusted networks — see AxioDBCloud above.


🤝 Contributing, License & Support

Contributing: we welcome contributions! See CONTRIBUTING.md for guidelines.

License: MIT. See LICENSE.

Requirements: Node.js ≥20.0.0, npm ≥6.0.0, yarn ≥1.0.0 (optional).

Documentation website: built with React 18 + TypeScript, Vite, TailwindCSS, and Lucide React. To run it locally:

cd Document
npm install
npm run dev

Available at http://localhost:5173.

Author: Ankan Saha

Support the project:

  • ⭐ Star the repository
  • 🐛 Report issues
  • 💡 Suggest features
  • 🤝 Contribute code
  • 💰 Sponsor the project

Acknowledgments: special thanks to all contributors and supporters of AxioDB — your feedback and contributions make this project better.