# Deno KV: How the Built-in Key-Value Store Works

> Explore Deno KV, the built-in key-value store offering atomic transactions and real-time watching with SQLite or HTTP backends. Learn how Deno KV works.

- Repository: [Deno/deno](https://github.com/denoland/deno)
- Tags: internals
- Published: 2026-02-26

---

**Deno KV is an unstable, built-in key-value storage API accessed via `Deno.openKv()` that provides atomic transactions, queue support, and real-time change watching across both local SQLite and remote HTTP backends.**

Deno KV is the native key-value storage solution embedded directly into the Deno runtime. As implemented in the `denoland/deno` repository, it exposes a JavaScript API that forwards operations to Rust-based storage backends, enabling developers to persist data without external dependencies or switch seamlessly to cloud-hosted storage by changing a single connection string.

## What is Deno KV?

Deno KV is exposed through the **unstable** function `Deno.openKv([path])`, which returns a `Deno.Kv` instance capable of storing arbitrary values under composite keys. The system supports binary buffers, strings, numbers, `Uint8Array`, and `bigint` values, and provides advanced features including range reads with configurable consistency, atomic write transactions, message queuing, and key change notifications.

Under the hood, Deno KV acts as a thin wrapper around a **storage backend** implementing the `denokv_proto::Database` trait. The runtime ships with two production-ready backends: a local **SQLite** implementation for development and edge scripts, and a **Remote** client that connects to hosted KV services via the KV Connect protocol.

## Architecture and Source Code Implementation

The Deno KV implementation follows a layered architecture that bridges JavaScript and Rust through Deno’s extension system.

### JavaScript Entry Point

The public API is defined in [`ext/kv/01_db.ts`](https://github.com/denoland/deno/blob/main/ext/kv/01_db.ts), which exports the `openKv` function and the `Kv` class. This JavaScript façade forwards all method calls—such as `get`, `set`, and `atomic()`—to native Rust operations registered via the `deno_kv` extension.

### Rust Operations Layer

In [`ext/kv/lib.rs`](https://github.com/denoland/deno/blob/main/ext/kv/lib.rs), the extension registers core operations including `op_kv_database_open`, `op_kv_snapshot_read`, `op_kv_atomic_write`, and `op_kv_enqueue`. These ops manage database lifecycle, range queries, transactional writes, and queue operations respectively. Each operation retrieves the `DatabaseResource` from the OpState’s resource table by its `ResourceId`, then delegates to the underlying backend implementation.

### Database Handler Abstraction

The `DatabaseHandler` trait defined in [`ext/kv/interface.rs`](https://github.com/denoland/deno/blob/main/ext/kv/interface.rs) abstracts backend initialization. Concrete implementations `SqliteDbHandler` and `RemoteDbHandler` provide the `open` method, which instantiates backends that satisfy the `denokv_proto::Database` protocol. This abstraction ensures the public JavaScript API remains identical regardless of whether data is stored locally or remotely.

## Storage Backends: SQLite vs. Remote

Deno KV supports two distinct storage backends selected automatically based on the `path` argument passed to `Deno.openKv()`:

**SQLite Backend (`denokv_sqlite`)**

- **Implementation**: [`ext/kv/sqlite.rs`](https://github.com/denoland/deno/blob/main/ext/kv/sqlite.rs) creates a `denokv_sqlite::Sqlite` instance with Write-Ahead Logging (WAL) enabled for concurrent read/write performance.
- **Usage**: Default when `path` is omitted (uses `./kv.sqlite3`) or points to a local file. Pass `:memory:` for ephemeral, in-memory storage ideal for testing.
- **Characteristics**: File-based, zero external dependencies, suitable for local development and edge deployments.

**Remote Backend (`denokv_remote`)**

- **Implementation**: [`ext/kv/remote.rs`](https://github.com/denoland/deno/blob/main/ext/kv/remote.rs) constructs a `denokv_remote::Remote` client that communicates via HTTP(S) using the KV Connect protocol.
- **Usage**: Activated when `path` is an HTTP(S) URL (e.g., `https://api.deno.com`).
- **Authentication**: Reads the `DENO_KV_ACCESS_TOKEN` environment variable for bearer token authentication.
- **Characteristics**: Enables distributed state across multiple runtime instances, requires network permissions.

## Core Operations and API Methods

All backends expose a consistent interface through the `Deno.Kv` class, implemented via the Rust ops in [`ext/kv/lib.rs`](https://github.com/denoland/deno/blob/main/ext/kv/lib.rs).

### Basic CRUD Operations

The `get`, `set`, and `list` methods handle single-key reads, writes, and range scans. Range reads support configurable consistency levels and are processed by `op_kv_snapshot_read` (lines 52-60 in [`ext/kv/lib.rs`](https://github.com/denoland/deno/blob/main/ext/kv/lib.rs)), which delegates to the backend’s `snapshot_read` method.

### Atomic Transactions

Atomic operations allow conditional writes using optimistic concurrency control. The `kv.atomic()` builder constructs transactions that validate `versionstamp` checks before applying mutations. The `op_kv_atomic_write` operation (lines 87-95 in [`ext/kv/lib.rs`](https://github.com/denoland/deno/blob/main/ext/kv/lib.rs)) executes these transactions with **serializable** isolation guarantees, ensuring strict ordering across concurrent clients.

### Message Queues

Deno KV implements durable queues through `enqueue` and `dequeue` methods. The `op_kv_enqueue` operation persists payloads with delivery deadlines and trigger keys, while `op_kv_dequeue_next_message` (lines 38-44 in [`ext/kv/lib.rs`](https://github.com/denoland/deno/blob/main/ext/kv/lib.rs)) retrieves messages for worker-style processing. Messages must be acknowledged via `finish()` to confirm successful handling.

### Change Watching

The `watch` method returns a `WatchStream` that emits notifications when specified keys change. This uses `op_kv_watch` and `op_kv_watch_next` (lines 75-85 in [`ext/kv/lib.rs`](https://github.com/denoland/deno/blob/main/ext/kv/lib.rs)) to maintain a persistent connection to backend event sources, enabling real-time reactive applications.

## Configuration and Operational Limits

Storage constraints are enforced via `KvConfig` defined in [`ext/kv/config.rs`](https://github.com/denoland/deno/blob/main/ext/kv/config.rs) (lines 3-15). This configuration specifies maximum key sizes, value sizes, and limits on the number of range entries and mutations per transaction. The runtime consults these limits during every operation to prevent resource exhaustion.

## Practical Usage Examples

### Opening a Local Database

```typescript
// Open default SQLite database (./kv.sqlite3)
const db = await Deno.openKv();

// Store a complex object under a composite key
await db.set(["users", 123], { name: "Alice", active: true });

const entry = await db.get(["users", 123]);
console.log(entry?.value); // { name: "Alice", active: true }

```

### In-Memory Testing

```typescript
// Create ephemeral database for unit tests
const kv = await Deno.openKv(":memory:");
await kv.set(["temp"], "data");

```

### Atomic Counter Increment

```typescript
const kv = await Deno.openKv(":memory:");
await kv.set(["counter"], 0);

const result = await kv.atomic()
  .check({ key: ["counter"], versionstamp: (await kv.get(["counter"])).versionstamp })
  .mutate({ key: ["counter"], type: "sum", value: 5 })
  .commit();

if (result.ok) {
  console.log("Increment succeeded");
} else {
  console.log("Concurrent modification detected");
}

```

### Enqueuing Background Tasks

```typescript
const kv = await Deno.openKv();

// Schedule payload with 60-second deadline, triggered by "tasks" key
await kv.enqueue(
  new Uint8Array([1, 2, 3]),
  60000,
  [["tasks"]],
);

// Worker loop
while (true) {
  const msg = await kv.dequeue("tasks");
  if (msg) {
    console.log("Processing:", msg.payload);
    await msg.finish(true); // Acknowledge completion
  }
}

```

### Watching Configuration Changes

```typescript
const kv = await Deno.openKv();

const watcher = await kv.watch(["config", "featureFlag"]);
for await (const change of watcher) {
  console.log("Flag updated:", change.value);
}

```

## Summary

- **Deno KV** is an unstable, embedded key-value store accessed via `Deno.openKv()` in the Deno runtime.
- The architecture separates JavaScript API surface ([`ext/kv/01_db.ts`](https://github.com/denoland/deno/blob/main/ext/kv/01_db.ts)) from Rust implementation ([`ext/kv/lib.rs`](https://github.com/denoland/deno/blob/main/ext/kv/lib.rs)) through the `deno_kv` extension system.
- **Two backends** provide deployment flexibility: SQLite for local/edge storage ([`ext/kv/sqlite.rs`](https://github.com/denoland/deno/blob/main/ext/kv/sqlite.rs)) and Remote for distributed cloud storage ([`ext/kv/remote.rs`](https://github.com/denoland/deno/blob/main/ext/kv/remote.rs)).
- **Atomic transactions** with versionstamp checks ensure serializable consistency across concurrent operations via `op_kv_atomic_write`.
- **Built-in queuing** supports durable message delivery with `enqueue` and `dequeue` operations backed by the same storage layer.
- **Change watching** enables real-time subscriptions to key modifications through async iterators.

## Frequently Asked Questions

### How do I enable Deno KV in my application?

Deno KV currently requires the `--unstable` flag when running the Deno runtime because the API is not yet finalized. Import the `Deno` namespace and call `await Deno.openKv()` to receive a `Deno.Kv` instance. No additional packages or external dependencies are required.

### What is the difference between SQLite and Remote backends?

The **SQLite backend** stores data in a local file using Write-Ahead Logging, making it ideal for development, testing, and single-node edge deployments. The **Remote backend** connects to a hosted KV service via HTTP using the KV Connect protocol, enabling data persistence across multiple distributed instances. Switching between them requires only changing the `path` argument from a file path to an HTTPS URL.

### Are there limits on key or value sizes in Deno KV?

Yes. The `KvConfig` struct in [`ext/kv/config.rs`](https://github.com/denoland/deno/blob/main/ext/kv/config.rs) defines operational limits including maximum key size, maximum value size, and constraints on the number of entries returned in range queries or modified in atomic transactions. These limits prevent abuse and ensure consistent performance across both SQLite and Remote backends.

### How does Deno KV handle concurrent write conflicts?

Deno KV implements **optimistic concurrency control** using versionstamps. When performing an atomic write via `kv.atomic().check().mutate().commit()`, the operation validates that the key’s versionstamp matches the expected value before applying mutations. If another writer modified the key concurrently, the check fails and the transaction returns `ok: false`, allowing the application to retry or resolve the conflict.