Typed Metadata
Type-safe VectorDB metadata with compile-time filter validation.
The VectorDB supports generic metadata types, giving you compile-time type checking on add(), search(), deleteWhere(), and filter expressions. No runtime cost -- all validation happens at the TypeScript level.
See it in action
Try Semantic Search and Product Search for working demos of these APIs.
Before and After
// No type parameter — metadata is Record<string, unknown>
const db = await createVectorDB({ name: 'docs', dimensions: 384 });
await db.add({
id: 'doc-1',
vector: embedding,
metadata: { anything: 'goes', count: 42 },
});
// Filters accept any key — typos compile fine but produce wrong results
const results = await db.search(query, {
k: 5,
filter: { categry: { $eq: 'tech' } }, // Typo not caught!
});interface ArticleMetadata {
title: string;
category: 'tech' | 'science' | 'business';
year: number;
tags: string[];
}
const db = await createVectorDB<ArticleMetadata>({
name: 'articles',
dimensions: 384,
});
await db.add({
id: 'doc-1',
vector: embedding,
metadata: {
title: 'Intro to ML',
category: 'tech', // Autocomplete shows 'tech' | 'science' | 'business'
year: 2025,
tags: ['ml', 'ai'],
},
});
// Filter keys are constrained to keyof ArticleMetadata
const results = await db.search(query, {
k: 5,
filter: { category: { $eq: 'tech' } }, // Type-safe!
});
// TypeScript error: 'categry' does not exist on type
// filter: { categry: { $eq: 'tech' } }TypedFilterQuery
When you pass a type parameter to createVectorDB<TMetadata>(), filter expressions use TypedFilterQuery<TMetadata> instead of the default untyped FilterQuery. This constrains filter keys to keyof TMetadata and validates operator value types.
// TypedFilterQuery<ArticleMetadata> only allows these keys:
const results = await db.search(query, {
k: 10,
filter: {
category: { $eq: 'tech' }, // string operators
year: { $gte: 2023 }, // number operators
tags: { $in: ['ml', 'ai'] }, // array operators
},
});
// deleteWhere is also type-safe
await db.deleteWhere({
year: { $lt: 2020 },
});Prop
Type
For the full list of filter operators, see Vector Database — Filter Operators.
Schema Validation
For runtime validation in addition to compile-time types, pass a schema to createVectorDB(). The schema uses the same ObjectSchema interface as generateObject().
With jsonSchema()
import { createVectorDB, jsonSchema } from '@localmode/core';
const db = await createVectorDB<ArticleMetadata>({
name: 'articles',
dimensions: 384,
schema: jsonSchema({
type: 'object',
properties: {
title: { type: 'string' },
category: { type: 'string', enum: ['tech', 'science', 'business'] },
year: { type: 'number' },
tags: { type: 'array', items: { type: 'string' } },
},
required: ['title', 'category', 'year', 'tags'],
}),
});
// Throws ValidationError at runtime if metadata doesn't match schema
await db.add({
id: 'doc-1',
vector: embedding,
metadata: { title: 123, category: 'invalid' }, // Runtime error!
});With Zod
import { createVectorDB, jsonSchema } from '@localmode/core';
import { z } from 'zod';
const ArticleSchema = z.object({
title: z.string(),
category: z.enum(['tech', 'science', 'business']),
year: z.number().int().min(1900),
tags: z.array(z.string()),
});
type ArticleMetadata = z.infer<typeof ArticleSchema>;
const db = await createVectorDB<ArticleMetadata>({
name: 'articles',
dimensions: 384,
schema: jsonSchema(ArticleSchema),
});Schema is Optional
Type-safe filters work with or without a schema. The generic type parameter handles compile-time safety. The schema adds optional runtime validation on add() and addMany().
Backward Compatibility
The default generic parameter is Record<string, unknown>, so existing code continues to work without changes:
// These are equivalent — both use untyped metadata
const db1 = await createVectorDB({ name: 'db', dimensions: 384 });
const db2 = await createVectorDB<Record<string, unknown>>({ name: 'db', dimensions: 384 });The FilterQuery type alias is kept for backward compatibility but is deprecated in favor of TypedFilterQuery<TMetadata>:
import type { TypedFilterQuery, FilterQuery } from '@localmode/core';
// Preferred
type MyFilter = TypedFilterQuery<ArticleMetadata>;
// Deprecated alias (still works)
type UntypedFilter = FilterQuery;Typed Search Results
When using a typed VectorDB, search results have typed metadata:
const results = await db.search(queryVector, { k: 5 });
results.forEach((r) => {
// r.metadata is typed as ArticleMetadata
console.log(r.metadata.title); // string
console.log(r.metadata.category); // 'tech' | 'science' | 'business'
console.log(r.metadata.year); // number
});Next Steps
Vector Database
Full VectorDB API reference including filters and HNSW config.
RAG
Build retrieval-augmented generation pipelines.
Storage
Storage adapters and persistence options.