| name | upstash-vector-db-skills |
| description | Upstash Vector DB setup, semantic search, namespaces, and embedding models (MixBread preferred). Use when building vector search features on Vercel. |
Links
Quick Setup
1. Create Vector Index (Upstash Console)
- Go to Upstash Console
- Create Vector Index: name, region (closest to app), type (Dense for semantic search)
- Select embedding model: MixBread AI recommended (or use Upstash built-in models)
- Copy
UPSTASH_VECTOR_REST_URL and UPSTASH_VECTOR_REST_TOKEN to .env
2. Install SDK
pnpm add @upstash/vector
3. Environment
UPSTASH_VECTOR_REST_URL=your_url
UPSTASH_VECTOR_REST_TOKEN=your_token
Code Examples
Initialize Client (Node.js / TypeScript)
import { Index } from "@upstash/vector";
const index = new Index({
url: process.env.UPSTASH_VECTOR_REST_URL,
token: process.env.UPSTASH_VECTOR_REST_TOKEN,
});
Upsert Documents (Auto-Embed)
When using an embedding model in the index, text is embedded automatically:
await index.upsert({
id: "doc-1",
data: "Upstash provides serverless vector database solutions.",
metadata: { source: "docs", category: "intro" },
});
await index.upsert([
{ id: "doc-2", data: "Vector search powers semantic similarity.", metadata: { source: "docs" } },
{ id: "doc-3", data: "MixBread AI provides high-quality embeddings.", metadata: { source: "blog" } },
]);
Query / Semantic Search
const results = await index.query({
data: "What is semantic search?",
topK: 5,
includeMetadata: true,
});
results.forEach((result) => {
console.log(`ID: ${result.id}, Score: ${result.score}, Metadata:`, result.metadata);
});
Using Namespaces (Data Isolation)
Namespaces partition a single index into isolated subsets. Useful for multi-tenant or multi-domain apps.
await index.namespace("blog").upsert({
id: "post-1",
data: "Next.js tutorial for Vercel deployment",
metadata: { author: "user-123" },
});
const blogResults = await index.namespace("blog").query({
data: "Vercel deployment",
topK: 3,
includeMetadata: true,
});
const namespaces = await index.listNamespaces();
console.log(namespaces);
await index.deleteNamespace("blog");
Full Semantic Search Example (Vercel Function)
import { Index } from "@upstash/vector";
export const config = {
runtime: "nodejs",
};
const index = new Index({
url: process.env.UPSTASH_VECTOR_REST_URL,
token: process.env.UPSTASH_VECTOR_REST_TOKEN,
});
export default async function handler(req, res) {
if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" });
}
const { query, namespace = "", topK = 5 } = req.body;
try {
const searchIndex = namespace ? index.namespace(namespace) : index;
const results = await searchIndex.query({
data: query,
topK,
includeMetadata: true,
});
return res.status(200).json({ results });
} catch (error) {
console.error("Search error:", error);
return res.status(500).json({ error: "Search failed" });
}
}
Index Operations
await index.reset();
await index.namespace("old-data").reset();
await index.delete("doc-1");
await index.delete(["doc-1", "doc-2", "doc-3"]);
Embedding Models
Available in Upstash
BAAI/bge-large-en-v1.5 (1024 dim, best performance, ~64.23 MTEB score)
BAAI/bge-base-en-v1.5 (768 dim, good balance)
BAAI/bge-small-en-v1.5 (384 dim, lightweight)
BAAI/bge-m3 (1024 dim, sparse + dense hybrid)
Recommended: MixBread AI
If using MixBread as your embedding provider:
- Create a MixBread API key at https://www.mixbread.ai/
- When creating your Upstash index, select MixBread as the embedding model.
- MixBread handles tokenization and semantic quality automatically.
- No extra setup needed in your code; use
index.upsert() / index.query() with text directly.
Best Practices
For Vercel Deployment
- Store credentials in Vercel Environment Variables (project settings or
.env.local).
- Use Edge Functions or Serverless Functions for low-latency access.
- Implement request rate limiting to stay within Upstash quotas.
Namespace Strategy
- Use namespaces to isolate data by tenant, domain, or use case.
- Example:
namespace("user-123") for per-user search.
- Clean up old namespaces to avoid storage bloat.
Query Performance
- Keep
topK reasonable (5–10 typically sufficient).
- Use metadata filtering to pre-filter results if possible.
- Upstash is eventually consistent; expect slight delays after upserts.
Error Handling
try {
const results = await index.query({
data: userQuery,
topK: 5,
includeMetadata: true,
});
} catch (error) {
if (error.status === 401) {
console.error("Invalid credentials");
} else if (error.status === 429) {
console.error("Rate limited");
} else {
console.error("Query error:", error);
}
}
Common Patterns
RAG (Retrieval Augmented Generation)
- Upsert documents / knowledge base into Upstash.
- On user query, retrieve top-k similar docs via semantic search.
- Pass retrieved docs + user query to LLM for better context.
const docs = await index.query({ data: userQuestion, topK: 3 });
const context = docs.map((d) => d.metadata?.text).join("\n");
Multi-Tenant Search
Use namespaces to isolate each tenant's vectors:
const userNamespace = `tenant-${userId}`;
await index.namespace(userNamespace).upsert({ id, data, metadata });
Batch Indexing
For bulk imports, upsert in batches:
const batchSize = 100;
for (let i = 0; i < documents.length; i += batchSize) {
const batch = documents.slice(i, i + batchSize);
await index.upsert(batch);
console.log(`Indexed batch ${i / batchSize + 1}`);
}
Troubleshooting
- No results returned: Ensure documents are indexed and embedding model is active.
- Slow queries: Check quota limits; consider upgrading plan or reducing dataset size.
- Stale data: Upstash is eventually consistent; wait 1–2 seconds before querying new inserts.
- Namespace not working: Ensure namespace exists (created on first upsert) or use the default
"".