DenoKV is a self-hosted backend for Deno KV, providing a JavaScript-first key-value database with ACID transactions and multiple consistency levels. This repository contains both the Rust server implementation and Node.js client libraries.
denokv- Main Rust server binary (HTTP server implementing KV Connect protocol)denokv_proto- Shared protocol definitions and Database traitdenokv_sqlite- SQLite-backed database implementationdenokv_remote- Remote client implementation for KV Connect protocoldenokv_timemachine- Backup and time-travel functionalitynpm/- Node.js client library with NAPI bindings
The project implements the KV Connect protocol (defined in proto/kv-connect.md), which consists of:
-
Metadata Exchange Protocol (JSON-based)
- Authentication and protocol version negotiation
- Database metadata retrieval
- Endpoint discovery
-
Data Path Protocol (Protobuf-based)
- Snapshot reads (
/snapshot_read) - Atomic writes (
/atomic_write) - Watch operations (
/watch)
- Snapshot reads (
The Database trait in proto/interface.rs defines the core operations:
snapshot_read()- Read operations with consistency levelsatomic_write()- ACID transactions with checks/mutations/enqueueswatch()- Real-time key change notifications
- Multi-threaded architecture with worker threads
- Connection pooling and retry logic
- Queue message handling with dead letter queues
- Sum operations for numeric values with clamping
- Versionstamp-based consistency and ordering
- HTTP client for KV Connect protocol
- Authentication handling with token management
- Streaming support for watch operations
- Retry logic with exponential backoff
# Rust toolchain (1.83+)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Node.js 18+ for npm package development
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
nvm install 18
# Protobuf compiler
# Ubuntu/Debian:
sudo apt-get install protobuf-compiler
# macOS:
brew install protobuf# Build all Rust components
cargo build
# Build release binary
cargo build --release
# Run tests
cargo test
# Build Docker image
docker build -t denokv .# Basic server
cargo run -- --sqlite-path ./data.db serve --access-token my-secret-token
# With Docker
docker run -p 4512:4512 -v ./data:/data ghcr.io/denoland/denokv \
--sqlite-path /data/denokv.sqlite serve --access-token my-token# Run integration tests (starts server, tests client)
cargo test --package denokv
# Run specific test
cargo test --package denokv basics
# Test npm package
cd npm/napi
npm test- Entry point with CLI argument parsing
- HTTP server setup using Axum framework
- Authentication middleware with Bearer tokens
- Endpoint handlers for KV Connect protocol
- S3 sync functionality for replica mode
interface.rs- Core Database trait and typesprotobuf.rs- Generated protobuf message typesconvert.rs- Conversion between protobuf and internal typeslimits.rs- Size and count limits for operations
lib.rs- Main Sqlite struct implementing Database traitbackend.rs- Low-level SQLite operations and schemasum_operand.rs- Sum operation logic with type checking
src/napi_based.ts- NAPI-based SQLite implementationsrc/remote.ts- HTTP client for remote databasessrc/in_memory.ts- Pure JS in-memory implementationsrc/kv_types.ts- TypeScript type definitions
- Define in protocol (
proto/interface.rs) - Implement in SQLite (
sqlite/backend.rs) - Add protobuf messages (
proto/schema/datapath.proto) - Update server handlers (
denokv/main.rs) - Add client support (
npm/src/)
# Enable debug logging
RUST_LOG=debug cargo run -- --sqlite-path ./test.db serve --access-token test
# Test with curl
curl -X POST http://localhost:4512/ \
-H "Authorization: Bearer test" \
-H "Content-Type: application/json" \
-d '{"supportedVersions": [2, 3]}'
# Inspect SQLite database
sqlite3 ./test.db ".schema"
sqlite3 ./test.db "SELECT * FROM data LIMIT 10;"- Batch operations - Use atomic writes for multiple operations
- Connection pooling - SQLite backend uses worker threads
- Consistency levels - Use eventual consistency for better performance
- Key design - Prefix keys for efficient range queries
- Start real server process
- Test full protocol flow
- Verify ACID properties
- Test error conditions
- Protocol conversion (
proto/) - SQLite operations (
sqlite/) - Client implementations (
npm/)
# Start server
cargo run -- --sqlite-path ./test.db serve --access-token test
# Test with Deno
deno eval "
const kv = await Deno.openKv('http://localhost:4512');
await kv.set(['test'], 'hello');
console.log(await kv.get(['test']));
"- Missing protobuf compiler - Install protobuf-compiler package
- NAPI build failures - Ensure Node.js 18+ and proper toolchain
- SQLite linking - May need sqlite3-dev package
- Permission denied - Check SQLite file permissions
- Port conflicts - Use different port with
--addr - Token authentication - Ensure consistent token usage
- Protobuf changes - Run
cargo buildto regenerate - Type mismatches - Check
proto/convert.rsfor conversion logic - Test failures - Ensure server is properly started in tests
- Rust: Follow standard rustfmt (run
cargo fmt) - TypeScript: Use Prettier (run
npm run format) - Commits: Use conventional commit messages
- Write tests for new functionality
- Update documentation if needed
- Run full test suite (
cargo test && cd npm/napi && npm test) - Check formatting (
cargo fmt && npm run format)
- Performance optimizations in SQLite backend
- Additional client libraries (Python, Go, etc.)
- Monitoring and metrics integration
- Backup and recovery improvements
- Documentation and examples
- 10-byte identifiers for ordering operations
- Monotonic ordering across all operations
- Consistency guarantees for reads
- Checks - Conditional operations based on current values
- Mutations - Set, delete, sum operations
- Enqueues - Queue message operations
- All-or-nothing transaction semantics
- Strong - Linearizable reads (default)
- Eventual - Eventually consistent reads (faster)
- Message enqueuing with deadlines
- Dead letter queues for failed messages
- Backoff intervals for retry logic
# Development
cargo run -- --help # Show CLI options
cargo test --package denokv -- --nocapture # Run tests with output
cargo clippy # Lint Rust code
# Server management
cargo run -- --sqlite-path ./db serve --access-token token --addr 0.0.0.0:4512
cargo run -- pitr list # List recoverable points
cargo run -- pitr checkout <version> # Checkout specific version
# NPM package
cd npm/napi
npm run build # Build native bindings
npm run test # Run tests
npm run format # Format code
# Docker
docker build -t denokv .
docker run -p 4512:4512 -v ./data:/data denokv --sqlite-path /data/db serve --access-token token- Deno KV Documentation: https://deno.com/kv
- KV Connect Protocol:
proto/kv-connect.md - Rust Async Book: https://rust-lang.github.io/async-book/
- Axum Framework: https://docs.rs/axum/
- SQLite Documentation: https://www.sqlite.org/docs.html
- NAPI-RS: https://napi.rs/
- Always test with real server - Unit tests aren't enough
- Understand the protocol - KV Connect is the foundation
- SQLite is single-writer - Design around this constraint
- Versionstamps are critical - They provide ordering guarantees
- Authentication is simple - Just Bearer tokens
- Error handling matters - Users depend on clear error messages
- Performance is important - This is a database, not just a toy
Remember: This is a production database system. Changes affect real users and their data. Test thoroughly, understand the implications, and always consider backward compatibility.