TypeScript client for encrypted vector database with maximum security and speed
Endee is a TypeScript client for a local vector database designed for maximum speed and efficiency. This package provides full type safety, modern ES module support, and optimized code for rapid Approximate Nearest Neighbor (ANN) searches on vector data.
- TypeScript First: Full type safety and IntelliSense support
- Fast ANN Searches: Efficient similarity searches on vector data
- Multiple Distance Metrics: Support for cosine, L2, and inner product distance metrics
- Hybrid Indexes: Support for dense vectors, sparse vectors, and hybrid (dense + sparse) searches
- Metadata Support: Attach and search with metadata and filters
- High Performance: Optimized for speed and efficiency
- Modern ES Modules: Native ES module support with proper tree-shaking
- Node.js >= 18.0.0
- Endee Local server running (see Quick Start)
``bash`
npm install endee
The Endee client connects to your local server (defaults to http://127.0.0.1:8080/api/v1):
`typescript
import { Endee, Precision } from 'endee';
// Connect to local Endee server (defaults to localhost:8080)
const client = new Endee();
`
Using Authentication? If your server has NDD_AUTH_TOKEN set, pass the same token when initializing:
`typescript`
const client = new Endee('your-auth-token');
If your server runs on a different port, use setBaseUrl():
`typescript
const client = new Endee();
// Set custom base URL for non-default port
client.setBaseUrl('http://0.0.0.0:8081/api/v1');
`
`typescript
import { Precision } from 'endee';
await client.createIndex({
name: 'my_vectors',
dimension: 384,
spaceType: 'cosine',
precision: Precision.INT8D,
});
`
Dense Index Parameters:
| Parameter | Description |
|-----------|-------------|
| name | Unique name for your index |dimension
| | Vector dimensionality (must match your embedding model's output) |spaceType
| | Distance metric - "cosine", "l2", or "ip" (inner product) |M
| | Graph connectivity - higher values increase recall but use more memory (default: 16) |efCon
| | Construction-time parameter - higher values improve index quality (default: 128) |precision
| | Quantization precision (default: Precision.INT8D) |
Hybrid indexes combine dense vector search with sparse vector search. Add the sparseDimension parameter:
`typescript`
await client.createIndex({
name: 'hybrid_index',
dimension: 384, // Dense vector dimension
sparseDimension: 30000, // Sparse vector dimension (vocabulary size)
spaceType: 'cosine',
precision: Precision.INT8D,
});
`typescript
// List all indexes
const indexes = await client.listIndexes();
// Get reference to an existing index
const index = await client.getIndex('my_vectors');
// Delete an index
await client.deleteIndex('my_vectors');
`
The index.upsert() method adds or updates vectors in an existing index.
`typescript
const index = await client.getIndex('my_index');
await index.upsert([
{
id: 'vec1',
vector: [0.1, 0.2, 0.3 / ... /],
meta: { title: 'First document' },
filter: { category: 'tech' },
},
{
id: 'vec2',
vector: [0.3, 0.4, 0.5 / ... /],
meta: { title: 'Second document' },
filter: { category: 'science' },
},
]);
`
Vector Object Fields:
| Field | Required | Description |
|-------|----------|-------------|
| id | Yes | Unique identifier for the vector |vector
| | Yes | Array of floats representing the embedding |meta
| | No | Arbitrary metadata object |filter
| | No | Key-value pairs for filtering during queries |
The index.query() method performs a similarity search.
`typescript
const results = await index.query({
vector: [0.15, 0.25 / ... /],
topK: 5,
ef: 128,
includeVectors: true,
});
for (const item of results) {
console.log(ID: ${item.id}, Similarity: ${item.similarity});`
}
Query Parameters:
| Parameter | Description |
|-----------|-------------|
| vector | Query vector (must match index dimension) |topK
| | Number of results to return (default: 10, max: 512) |ef
| | Search quality parameter (default: 128, max: 1024) |includeVectors
| | Include vector data in results (default: false) |
Use the filter parameter to restrict results. All filters are combined with logical AND.
`typescript`
const results = await index.query({
vector: [0.15, 0.25 / ... /],
topK: 5,
filter: [
{ category: { $eq: 'tech' } },
{ score: { $range: [80, 100] } },
],
});
| Operator | Description | Example |
|----------|-------------|---------|
| $eq | Exact match | { status: { $eq: 'published' } } |$in
| | Match any in list | { tags: { $in: ['ai', 'ml'] } } |$range
| | Numeric range (inclusive) | { score: { $range: [70, 95] } } |
> Note: The $range operator supports values within [0 - 999]. Normalize larger values before upserting.
Provide both dense vectors and sparse representations:
`typescript
const index = await client.getIndex('hybrid_index');
await index.upsert([
{
id: 'doc1',
vector: [0.1, 0.2 / ... /], // Dense vector
sparseIndices: [10, 50, 200], // Non-zero term positions
sparseValues: [0.8, 0.5, 0.3], // Weights for each position
meta: { title: 'Document 1' },
},
{
id: 'doc2',
vector: [0.3, 0.4 / ... /],
sparseIndices: [15, 100, 500],
sparseValues: [0.9, 0.4, 0.6],
meta: { title: 'Document 2' },
},
]);
`
Hybrid Vector Fields:
| Field | Required | Description |
|-------|----------|-------------|
| id | Yes | Unique identifier |vector
| | Yes | Dense embedding vector |sparseIndices
| | Yes (hybrid) | Non-zero term positions in sparse vector |sparseValues
| | Yes (hybrid) | Weights for each sparse index |meta
| | No | Metadata dictionary |filter
| | No | Filter fields |
> Important: sparseIndices and sparseValues must have the same length. Values in sparseIndices must be within [0, sparseDimension).
Provide both dense and sparse query vectors:
`typescript
const results = await index.query({
vector: [0.15, 0.25 / ... /], // Dense query
sparseIndices: [10, 100, 300], // Sparse query positions
sparseValues: [0.7, 0.5, 0.4], // Sparse query weights
topK: 5,
});
for (const item of results) {
console.log(ID: ${item.id}, Similarity: ${item.similarity});`
}
You can also query with:
- Dense only: Provide only vectorsparseIndices
- Sparse only: Provide only and sparseValues
- Hybrid: Provide all three for combined results
typescript
await index.deleteVector('vec1');
`
$3
Delete all vectors matching specific filters.
`typescript
await index.deleteWithFilter([{'category': {'$eq' : 'tech'}}]);
`$3
Delete an entire Index.
`typescript
await client.deleteIndex('my_index');
`> Warning: Deletion operations are irreversible.
Additional Operations
$3
`typescript
const vector = await index.getVector('vec1');
`$3
`typescript
const info = index.describe();
console.log(info);
// { name, spaceType, dimension, sparseDimension, isHybrid, count, precision, M }
`Precision Options
Endee supports different quantization precision levels:
`typescript
import { Precision } from 'endee';Precision.BINARY; // Binary quantization (1-bit) - smallest storage, fastest search
Precision.INT8D; // 8-bit integer quantization (default) - balanced performance
Precision.INT16D; // 16-bit integer quantization - higher precision
Precision.FLOAT16; // 16-bit floating point - good balance
Precision.FLOAT32; // 32-bit floating point - highest precision
`Choosing Precision:
-
BINARY: Best for very large datasets where speed and storage are critical
- INT8D (default): Recommended for most use cases - good balance of accuracy and performance
- INT16D: When you need better accuracy than INT8D but less storage than FLOAT32
- FLOAT16: Good compromise between precision and storage for embeddings
- FLOAT32: When you need maximum precision and storage is not a concernComplete Example
`typescript
import { Endee, Precision } from 'endee';// Initialize client
const client = new Endee();
// Create a dense index
await client.createIndex({
name: 'documents',
dimension: 384,
spaceType: 'cosine',
precision: Precision.INT8D,
});
// Get the index
const index = await client.getIndex('documents');
// Add vectors
await index.upsert([
{
id: 'doc1',
vector: [0.1, 0.2 / ... 384 dimensions /],
meta: { title: 'First Document' },
filter: { category: 'tech' },
},
{
id: 'doc2',
vector: [0.3, 0.4 / ... 384 dimensions /],
meta: { title: 'Second Document' },
filter: { category: 'science' },
},
]);
// Query the index
const results = await index.query({
vector: [0.15, 0.25 / ... /],
topK: 5,
});
for (const item of results) {
console.log(
ID: ${item.id}, Similarity: ${item.similarity});
}
`API Reference
$3
| Method | Description |
|--------|-------------|
|
createIndex(options) | Create a new index (add sparseDimension for hybrid) |
| listIndexes() | List all indexes |
| deleteIndex(name) | Delete an index |
| getIndex(name) | Get reference to an index |
| setBaseUrl(url) | Set a custom base URL |$3
| Method | Description |
|--------|-------------|
|
upsert(vectors) | Insert or update vectors |
| query(options) | Search for similar vectors |
| deleteVector(id) | Delete a vector by ID |
| deleteWithFilter(filter) | Delete vectors by Filter |
| getVector(id) | Get a vector by ID |
| describe() | Get index info |TypeScript Types
The package includes comprehensive TypeScript types:
`typescript
import type {
VectorItem,
QueryOptions,
QueryResult,
CreateIndexOptions,
IndexDescription,
SpaceType,
Precision,
} from 'endee';
``MIT
Pankaj Singh