High-performance Node.js bindings for router-core vector database and neural routing engine.
NAPI-RS powered bindings bringing Rust-level performance to JavaScript/TypeScript with zero-copy buffer sharing and async/await support.
router-ffi provides seamless Node.js integration for the router-core vector database through NAPI-RS, enabling JavaScript and TypeScript applications to leverage Rust's blazing-fast performance for vector similarity search, neural routing, and embedding operations.
- 🚀 Native Performance: Direct Rust execution with minimal overhead
- ⚡ Zero-Copy: Float32Array buffers shared directly with Rust
- 🔄 Async/Await: Non-blocking operations using Tokio runtime
- 🎯 Type Safe: Complete TypeScript definitions auto-generated from Rust
- 🌐 Cross-Platform: Linux, macOS, Windows (x64 and ARM64)
- 🧠 Neural Routing: Advanced inference and routing capabilities
- 💾 Memory Efficient: HNSW indexing with 4-32x compression
- Vector Operations: Insert, search, delete with sub-millisecond latency
- Distance Metrics: Euclidean, Cosine, Dot Product, Manhattan
- HNSW Indexing: Sub-millisecond search with 95%+ recall
- Async API: Full async/await support for all operations
- Batch Operations: Efficient bulk insert and search
- Metadata Support: Store and filter by JSON metadata
- Persistent Storage: Disk-based storage with memory-mapped I/O
- Neural Routing: Intelligent request routing and load balancing
- SIMD Optimizations: Hardware-accelerated distance calculations
- Product Quantization: 4-32x memory compression
- Thread Safety: Arc-based concurrency for multi-threaded Node.js
- Error Handling: Proper JavaScript error propagation from Rust
npm install router-ffi- Node.js: 18.0 or higher
- Rust: 1.77+ (for building from source)
- Platform: Linux, macOS, or Windows (x64/ARM64)
const { VectorDB, DistanceMetric } = require('router-ffi');
// Create a vector database
const db = new VectorDB({
dimensions: 384,
maxElements: 10000,
distanceMetric: DistanceMetric.Cosine,
hnswM: 32,
hnswEfConstruction: 200,
hnswEfSearch: 100,
storagePath: './vectors.db'
});
// Insert a vector
const vector = new Float32Array([0.1, 0.2, 0.3, /* ... 384 dimensions */]);
const id = db.insert('doc1', vector);
console.log(`Inserted: ${id}`);
// Search for similar vectors
const query = new Float32Array([0.1, 0.2, 0.3, /* ... */]);
const results = db.search(query, 10);
results.forEach(result => {
console.log(`ID: ${result.id}, Score: ${result.score}`);
});
// Get database statistics
const count = db.count();
const allIds = db.getAllIds();
console.log(`Database contains ${count} vectors`);const { VectorDB } = require('router-ffi');
async function main() {
const db = new VectorDB({
dimensions: 768,
distanceMetric: 'Cosine',
storagePath: './async-vectors.db'
});
// Async insert (non-blocking)
const vector = new Float32Array(768).fill(0.5);
const id = await db.insertAsync('doc1', vector);
console.log(`Inserted: ${id}`);
// Async search (non-blocking)
const query = new Float32Array(768).fill(0.5);
const results = await db.searchAsync(query, 10);
for (const result of results) {
console.log(`ID: ${result.id}, Score: ${result.score}`);
}
}
main().catch(console.error);import { VectorDB, DistanceMetric, DbOptions, SearchResultJS } from 'router-ffi';
// Type-safe configuration
const options: DbOptions = {
dimensions: 384,
maxElements: 50000,
distanceMetric: DistanceMetric.Cosine,
hnswM: 32,
hnswEfConstruction: 200,
hnswEfSearch: 100,
storagePath: './typed-vectors.db'
};
const db = new VectorDB(options);
// Type-safe operations
const vector: Float32Array = new Float32Array(384);
const id: string = db.insert('doc1', vector);
const results: SearchResultJS[] = db.search(vector, 10);
results.forEach((result: SearchResultJS) => {
console.log(`${result.id}: ${result.score}`);
});Main vector database class providing core operations.
new VectorDB(options: DbOptions)DbOptions:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
dimensions |
number |
Yes | - | Vector dimensionality |
maxElements |
number |
No | 1,000,000 | Maximum number of vectors |
distanceMetric |
DistanceMetric |
No | Cosine |
Distance metric |
hnswM |
number |
No | 32 | HNSW connections per node |
hnswEfConstruction |
number |
No | 200 | HNSW construction quality |
hnswEfSearch |
number |
No | 100 | HNSW search quality |
storagePath |
string |
No | ./router.db |
Database file path |
insert(id: string, vector: Float32Array): stringInsert a vector synchronously. Returns the vector ID.
Example:
const id = db.insert('doc1', new Float32Array([0.1, 0.2, 0.3]));async insertAsync(id: string, vector: Float32Array): Promise<string>Insert a vector asynchronously (non-blocking). Returns a Promise with the vector ID.
Example:
const id = await db.insertAsync('doc1', new Float32Array([0.1, 0.2, 0.3]));search(queryVector: Float32Array, k: number): SearchResultJS[]Search for similar vectors synchronously. Returns top-k results sorted by similarity.
Returns: Array of SearchResultJS objects:
id(string): Vector IDscore(number): Distance score (lower is more similar)
Example:
const results = db.search(new Float32Array([0.1, 0.2, 0.3]), 10);async searchAsync(queryVector: Float32Array, k: number): Promise<SearchResultJS[]>Search for similar vectors asynchronously (non-blocking).
Example:
const results = await db.searchAsync(new Float32Array([0.1, 0.2, 0.3]), 10);delete(id: string): booleanDelete a vector by ID. Returns true if deleted, false if not found.
Example:
const deleted = db.delete('doc1');count(): numberGet the total number of vectors in the database.
Example:
const totalVectors = db.count();
console.log(`Database contains ${totalVectors} vectors`);getAllIds(): string[]Get all vector IDs in the database.
Example:
const ids = db.getAllIds();
console.log(`IDs: ${ids.join(', ')}`);Enum defining supported distance metrics:
enum DistanceMetric {
Euclidean = "Euclidean",
Cosine = "Cosine",
DotProduct = "DotProduct",
Manhattan = "Manhattan"
}Choosing a Metric:
- Cosine: Best for normalized embeddings (most common)
- Euclidean: Standard L2 distance
- DotProduct: Efficient for pre-normalized vectors
- Manhattan: L1 distance, robust to outliers
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Install Node.js dependencies
npm install# Build the native module (release mode)
npm run build
# Build with debug symbols
npm run build:debug
# Clean build artifacts
npm run clean
# Run tests
npm test
# Format code
cargo fmt --all
# Lint
cargo clippy --all -- -D warningsBuild for multiple platforms using NAPI-RS:
# Linux x64
npm run build -- --target x86_64-unknown-linux-gnu
# Linux ARM64
npm run build -- --target aarch64-unknown-linux-gnu
# macOS x64
npm run build -- --target x86_64-apple-darwin
# macOS ARM64 (M1/M2)
npm run build -- --target aarch64-apple-darwin
# Windows x64
npm run build -- --target x86_64-pc-windows-msvc10,000 vectors (384D):
Operation Throughput Latency (avg)
------------------------------------------------
Insert (sync) ~2,000/sec 0.5ms
Insert (async) ~5,000/sec 0.2ms
Search (k=10) ~10,000/sec 0.1ms
Batch Insert ~8,000/sec 0.125ms
1,000,000 vectors (384D):
Operation Throughput Latency (avg)
------------------------------------------------
Insert (sync) ~1,000/sec 1.0ms
Insert (async) ~3,000/sec 0.33ms
Search (k=10) ~5,000/sec 0.2ms
Search (k=100) ~2,000/sec 0.5ms
Library Search Latency Memory (1M vectors) Language
-------------------------------------------------------------------
router-ffi 0.2ms ~600MB Rust → Node.js
Pinecone ~2ms Cloud only Hosted
Qdrant ~1ms ~1.5GB Rust
ChromaDB ~50ms ~3GB Python
FAISS ~0.5ms ~1GB C++ → Python
- Use Async Operations:
insertAsyncandsearchAsyncfor better throughput - Batch Inserts: Group multiple inserts for 3-4x better performance
- Tune HNSW Parameters:
- Higher
hnswM= better recall, more memory - Higher
efConstruction= better index quality, slower build - Higher
efSearch= better accuracy, slower search
- Higher
- Choose Distance Metric:
Cosinewith pre-normalized vectors is fastest - Use Float32Array: Direct buffer sharing avoids copies
const { VectorDB } = require('router-ffi');
// Create embeddings database
const db = new VectorDB({
dimensions: 1536, // OpenAI ada-002
distanceMetric: 'Cosine',
storagePath: './embeddings.db'
});
// Store document embeddings
async function indexDocument(docId, embedding) {
await db.insertAsync(docId, new Float32Array(embedding));
}
// Retrieve relevant documents
async function retrieveContext(queryEmbedding, topK = 5) {
const results = await db.searchAsync(
new Float32Array(queryEmbedding),
topK
);
return results.map(r => r.id);
}// Index text embeddings
const documents = [
{ id: 'doc1', text: 'Machine learning basics', embedding: [...] },
{ id: 'doc2', text: 'Deep learning tutorial', embedding: [...] },
{ id: 'doc3', text: 'Neural networks explained', embedding: [...] }
];
for (const doc of documents) {
await db.insertAsync(doc.id, new Float32Array(doc.embedding));
}
// Search by semantic similarity
const query = 'AI fundamentals';
const queryEmbedding = await getEmbedding(query);
const results = await db.searchAsync(new Float32Array(queryEmbedding), 3);// Store user/item embeddings
const userEmbeddings = new Map();
const itemEmbeddings = new Map();
// Index items
for (const [itemId, embedding] of itemEmbeddings) {
await db.insertAsync(`item_${itemId}`, new Float32Array(embedding));
}
// Find similar items
function recommendSimilar(itemId, count = 10) {
const embedding = itemEmbeddings.get(itemId);
const results = db.search(new Float32Array(embedding), count + 1);
return results.slice(1); // Exclude self
}// Store agent experiences
class AgentMemory {
constructor(dimensions) {
this.db = new VectorDB({
dimensions,
distanceMetric: 'Cosine',
storagePath: './agent-memory.db'
});
}
async remember(experience, embedding) {
const id = `exp_${Date.now()}`;
await this.db.insertAsync(id, new Float32Array(embedding));
return id;
}
async recall(queryEmbedding, count = 5) {
return await this.db.searchAsync(
new Float32Array(queryEmbedding),
count
);
}
}router-ffi uses Rust's Arc<T> for thread-safe reference counting, making it safe to use across Node.js worker threads:
const { Worker } = require('worker_threads');
const { VectorDB } = require('router-ffi');
// Main thread
const db = new VectorDB({ dimensions: 384 });
// Workers can safely share the database
const worker = new Worker('./worker.js');- Zero-Copy Buffers: Float32Array data is shared directly between JavaScript and Rust
- Arc Reference Counting: Automatic cleanup when JavaScript objects are garbage collected
- Memory-Mapped I/O: Efficient disk-based storage with OS-level caching
- Product Quantization: 4-32x compression for large datasets
- Reuse VectorDB Instances: Creating new instances is expensive
- Use Float32Array: Native typed arrays avoid data copying
- Async for Large Batches: Prevents blocking the event loop
- Close When Done: Allow garbage collection to free resources
// Good: Reuse instance
const db = new VectorDB({ dimensions: 384 });
for (let i = 0; i < 1000; i++) {
await db.insertAsync(`doc${i}`, vector);
}
// Bad: Creating new instances
for (let i = 0; i < 1000; i++) {
const db = new VectorDB({ dimensions: 384 }); // Expensive!
await db.insertAsync(`doc${i}`, vector);
}| Platform | Architecture | Status | Notes |
|---|---|---|---|
| Linux | x86_64 | ✅ Supported | glibc 2.17+ |
| Linux | aarch64 | ✅ Supported | ARM64 servers |
| macOS | x86_64 | ✅ Supported | Intel Macs |
| macOS | aarch64 | ✅ Supported | M1/M2/M3 Macs |
| Windows | x86_64 | ✅ Supported | MSVC runtime |
| Windows | aarch64 | ARM64 Windows |
- Minimum: Node.js 18.0
- Recommended: Node.js 20 LTS or 22 LTS
- Maximum: Latest stable release
NAPI-RS provides pre-built binaries for common platforms. If your platform isn't supported, the module will compile from source automatically.
Error: cargo not found
Install Rust toolchain:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/envError: NAPI-RS build failed
Update NAPI-RS CLI:
npm install -g @napi-rs/cli@latestError: Cannot find native module
Rebuild the native module:
npm rebuild router-ffiError: Vector dimension mismatch
Ensure all vectors have the same dimensions as specified in DbOptions:
const db = new VectorDB({ dimensions: 384 });
// ✅ Correct
db.insert('doc1', new Float32Array(384));
// ❌ Wrong - dimension mismatch
db.insert('doc2', new Float32Array(768)); // Error!Slow search performance
- Increase
hnswEfSearchfor better recall - Use
searchAsyncinstead ofsearch - Check if HNSW index is built (insert >100 vectors)
- Consider using Product Quantization for large datasets
High memory usage
- Reduce
maxElementsif you don't need it - Enable quantization (requires router-core configuration)
- Use smaller
hnswMvalue (trades accuracy for memory)
The HNSW (Hierarchical Navigable Small World) index provides fast approximate nearest neighbor search:
const db = new VectorDB({
dimensions: 384,
hnswM: 32, // Connections per node (16-64)
hnswEfConstruction: 200, // Build quality (100-500)
hnswEfSearch: 100 // Search quality (10-200)
});Parameter Guidelines:
| Parameter | Low Value | High Value | Trade-off |
|---|---|---|---|
hnswM |
16 | 64 | Memory vs Recall |
efConstruction |
100 | 500 | Speed vs Quality |
efSearch |
10 | 200 | Speed vs Accuracy |
// Cosine Similarity (angle between vectors)
// Range: [0, 2], lower is more similar
// Best for: Normalized embeddings, semantic search
const db1 = new VectorDB({ distanceMetric: 'Cosine' });
// Euclidean Distance (L2 norm)
// Range: [0, ∞), lower is more similar
// Best for: Spatial data, general purpose
const db2 = new VectorDB({ distanceMetric: 'Euclidean' });
// Dot Product (inner product)
// Range: (-∞, ∞), higher is more similar
// Best for: Pre-normalized vectors
const db3 = new VectorDB({ distanceMetric: 'DotProduct' });
// Manhattan Distance (L1 norm)
// Range: [0, ∞), lower is more similar
// Best for: Robust to outliers
const db4 = new VectorDB({ distanceMetric: 'Manhattan' });// Efficient batch insert
async function batchInsert(vectors) {
const promises = vectors.map((vec, idx) =>
db.insertAsync(`doc${idx}`, new Float32Array(vec))
);
return await Promise.all(promises);
}
// Parallel search
async function batchSearch(queries, k = 10) {
const promises = queries.map(query =>
db.searchAsync(new Float32Array(query), k)
);
return await Promise.all(promises);
}Complete examples are available in the examples directory:
- basic.js: Simple insert and search operations
- async.js: Async/await patterns
- typescript.ts: TypeScript integration
- rag-system.js: RAG implementation
- semantic-search.js: Semantic search engine
- benchmark.js: Performance testing
Run examples:
npm run build
node examples/basic.js
node examples/async.js
ts-node examples/typescript.tsrouter-ffi wraps the router-core Rust crate, providing:
- Full API compatibility with router-core
- Zero-overhead FFI through NAPI-RS
- Automatic memory management
- Thread-safe operations
- Async runtime integration
See router-core for core implementation details.
- router-core: Core vector database engine
- router-cli: Command-line interface
- router-wasm: WebAssembly bindings
- ruvector-node: Node.js bindings for ruvector-core
Contributions are welcome! See CONTRIBUTING.md for guidelines.
# Clone repository
git clone https://github.com/ruvnet/ruvector.git
cd ruvector/crates/router-ffi
# Install dependencies
npm install
# Build in development mode
npm run build:debug
# Run tests
npm test
# Format code
cargo fmt --all
# Lint
cargo clippy --all -- -D warnings# Run all tests
npm test
# Run specific test
npm test -- --grep "search"
# Run benchmarks
npm run benchMIT License - see LICENSE for details.
Built with cutting-edge technologies:
- NAPI-RS: High-performance Rust bindings for Node.js
- router-core: Core vector database engine
- Tokio: Asynchronous runtime for Rust
- SimSIMD: SIMD-accelerated similarity metrics
- HNSW: Hierarchical Navigable Small World graphs
- Documentation: github.com/ruvnet/ruvector
- Issues: GitHub Issues
- Discord: Join our community
- Twitter: @ruvnet
- Enterprise: enterprise@ruv.io