The Pinecone TypeScript SDK is fully async and uses modern async/await patterns. This guide covers best practices for working with asynchronous operations.
All SDK methods return Promises and should be awaited:
import { Pinecone } from '@pinecone-database/pinecone';
async function basicExample() {
const pc = new Pinecone({ apiKey: 'YOUR_API_KEY' });
// Always await SDK operations
const list = await pc.indexes.list();
console.log(list.indexes);
}
basicExample();
Wrap async operations in try/catch blocks to handle errors. The SDK's error
classes are exported under the Errors namespace, and under strict mode a
catch binding is typed unknown, so narrow it with instanceof before
reading any properties off it:
import { Pinecone, Errors } from '@pinecone-database/pinecone';
async function errorHandlingExample() {
const pc = new Pinecone({ apiKey: 'YOUR_API_KEY' });
try {
const indexModel = await pc.indexes.describe('my-index');
const index = pc.index({ host: indexModel.host });
const results = await index.query({
vector: [0.1, 0.2, 0.3],
topK: 10,
});
console.log(results);
} catch (error) {
if (error instanceof Errors.PineconeConnectionError) {
console.error('Failed to connect to Pinecone:', error.message);
// Handle connection error
} else {
console.error('Unexpected error:', error);
// Handle other errors
}
}
}
errorHandlingExample();
When you need to perform multiple independent operations, use Promise.all for better performance:
import { Pinecone } from '@pinecone-database/pinecone';
async function parallelOperations() {
const pc = new Pinecone({ apiKey: 'YOUR_API_KEY' });
const indexModel = await pc.indexes.describe('my-index');
const index = pc.index({ host: indexModel.host });
// Fetch multiple vectors in parallel
const [vec1, vec2, vec3] = await Promise.all([
index.fetch({ ids: ['1'] }),
index.fetch({ ids: ['2'] }),
index.fetch({ ids: ['3'] }),
]);
// Or perform different operations in parallel
const [stats, description, namespaces] = await Promise.all([
index.describeIndexStats(),
pc.indexes.describe('my-index'),
index.listNamespaces(),
]);
console.log({ stats, description, namespaces });
}
parallelOperations();
When upserting or querying multiple items, batch them for efficiency:
import { Pinecone, PineconeRecord } from '@pinecone-database/pinecone';
async function batchOperations() {
const pc = new Pinecone({ apiKey: 'YOUR_API_KEY' });
const indexModel = await pc.indexes.describe('my-index');
const index = pc.index({ host: indexModel.host });
// Prepare all records first
const records: PineconeRecord[] = Array.from({ length: 100 }, (_, i) => ({
id: `vec${i}`,
values: Array.from({ length: 1536 }, () => Math.random()),
metadata: { batch: 1, index: i },
}));
// Upsert in batches of 100 (recommended batch size)
const batchSize = 100;
for (let i = 0; i < records.length; i += batchSize) {
const batch = records.slice(i, i + batchSize);
await index.upsert({ records: batch });
console.log(`Upserted batch ${i / batchSize + 1}`);
}
}
batchOperations();
When operations depend on each other, chain them sequentially:
import { Pinecone } from '@pinecone-database/pinecone';
async function sequentialOperations() {
const pc = new Pinecone({ apiKey: 'YOUR_API_KEY' });
// Create index and wait for it to be ready
await pc.indexes.create({
name: 'new-index',
dimension: 1536,
spec: {
serverless: {
cloud: 'aws',
region: 'us-east-1',
},
},
waitUntilReady: true,
});
// Now we can safely perform data operations
const index = pc.index({ name: 'new-index' });
await index.upsert({
records: [{ id: '1', values: [0.1, 0.2, 0.3] }],
});
// Query the data we just upserted
const results = await index.query({
id: '1',
topK: 5,
});
console.log(results);
}
sequentialOperations();
Implement custom retry logic for transient failures:
import { Pinecone, Errors } from '@pinecone-database/pinecone';
async function retryOperation<T>(
operation: () => Promise<T>,
maxRetries: number = 3,
delayMs: number = 1000,
): Promise<T> {
let lastError: Error | undefined;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error as Error;
if (
error instanceof Errors.PineconeConnectionError &&
attempt < maxRetries - 1
) {
console.log(
`Attempt ${attempt + 1} failed, retrying in ${delayMs}ms...`,
);
await new Promise((resolve) => setTimeout(resolve, delayMs));
delayMs *= 2; // Exponential backoff
} else {
throw error;
}
}
}
throw lastError ?? new Error('Operation failed after all retry attempts');
}
// Usage
async function queryWithRetry() {
const pc = new Pinecone({ apiKey: 'YOUR_API_KEY' });
const indexModel = await pc.indexes.describe('my-index');
const index = pc.index({ host: indexModel.host });
const results = await retryOperation(
() =>
index.query({
vector: [0.1, 0.2, 0.3],
topK: 10,
}),
3, // Max 3 retries
1000, // Start with 1 second delay
);
return results;
}
Implement timeouts for long-running operations:
import { Pinecone } from '@pinecone-database/pinecone';
async function withTimeout<T>(
promise: Promise<T>,
timeoutMs: number,
errorMessage: string = 'Operation timed out',
): Promise<T> {
const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => reject(new Error(errorMessage)), timeoutMs);
});
return Promise.race([promise, timeoutPromise]);
}
// Usage
async function queryWithTimeout() {
const pc = new Pinecone({ apiKey: 'YOUR_API_KEY' });
const indexModel = await pc.indexes.describe('my-index');
const index = pc.index({ host: indexModel.host });
try {
const results = await withTimeout(
index.query({
vector: [0.1, 0.2, 0.3],
topK: 10,
}),
5000, // 5 second timeout
'Query operation timed out after 5 seconds',
);
console.log(results);
} catch (error) {
console.error(error);
}
}
queryWithTimeout();
When working with streaming Assistant responses, use async iteration:
import { Pinecone } from '@pinecone-database/pinecone';
async function streamingExample() {
const pc = new Pinecone({ apiKey: 'YOUR_API_KEY' });
const assistant = pc.assistant({ name: 'my-assistant' });
const stream = await assistant.chatStream({
messages: [
{
role: 'user',
content: 'Tell me a story',
},
],
});
// Use for-await-of to process stream chunks
for await (const chunk of stream) {
if (chunk.type === 'content_chunk' && chunk.delta?.content) {
process.stdout.write(chunk.delta.content);
}
}
}
streamingExample();
While async/await is preferred, you can also use promise chaining:
import { Pinecone } from '@pinecone-database/pinecone';
const pc = new Pinecone({ apiKey: 'YOUR_API_KEY' });
pc.indexes
.create({
name: 'new-index',
dimension: 1536,
spec: {
serverless: {
cloud: 'aws',
region: 'us-east-1',
},
},
waitUntilReady: true,
})
.then(() => {
const index = pc.index({ name: 'new-index' });
return index.upsert({
records: [{ id: '1', values: [0.1, 0.2, 0.3] }],
});
})
.then(() => {
console.log('Upsert complete');
})
.catch((error) => {
console.error('Error:', error);
});
Promise.all for independent operationsFor more information on error handling, see Error Handling.