> algolia-sdk-patterns
Apply production-ready algoliasearch v5 patterns: singleton client, typed search, error handling, and batch operations. Use when implementing Algolia integrations, refactoring SDK usage, or establishing team coding standards. Trigger: "algolia SDK patterns", "algolia best practices", "algolia code patterns", "idiomatic algolia".
curl "https://skillshub.wtf/jeremylongshore/claude-code-plugins-plus-skills/algolia-sdk-patterns?format=md"Algolia SDK Patterns
Overview
Production-ready patterns for algoliasearch v5. Key architectural change from v4: all methods live on the client directly — no more client.initIndex(). Index name is passed as a parameter to every call.
Prerequisites
algoliasearchv5+ installed- Completed
algolia-install-authsetup - TypeScript project (patterns work in JS too, you just lose type safety)
Instructions
Pattern 1: Typed Singleton Client
// src/algolia/client.ts
import { algoliasearch, type Algoliasearch } from 'algoliasearch';
let _client: Algoliasearch | null = null;
export function getClient(): Algoliasearch {
if (!_client) {
const appId = process.env.ALGOLIA_APP_ID;
const apiKey = process.env.ALGOLIA_ADMIN_KEY;
if (!appId || !apiKey) {
throw new Error(
'ALGOLIA_APP_ID and ALGOLIA_ADMIN_KEY must be set. '
+ 'Get them from dashboard.algolia.com > Settings > API Keys'
);
}
_client = algoliasearch(appId, apiKey);
}
return _client;
}
// For testing: reset singleton
export function resetClient(): void {
_client = null;
}
Pattern 2: Typed Search Results
// src/algolia/types.ts
// Define your record shape — extends Algolia's Hit type
interface Product {
objectID: string;
name: string;
category: string;
price: number;
description: string;
image_url: string;
}
// src/algolia/search.ts
import { getClient } from './client';
export async function searchProducts(
query: string,
options?: {
filters?: string;
facetFilters?: string[][];
hitsPerPage?: number;
page?: number;
}
) {
const client = getClient();
const { hits, nbHits, nbPages, page } = await client.searchSingleIndex<Product>({
indexName: 'products',
searchParams: {
query,
filters: options?.filters,
facetFilters: options?.facetFilters,
hitsPerPage: options?.hitsPerPage ?? 20,
page: options?.page ?? 0,
attributesToRetrieve: ['name', 'category', 'price', 'image_url'],
attributesToHighlight: ['name', 'description'],
},
});
return { hits, totalHits: nbHits, totalPages: nbPages, currentPage: page };
}
// Usage: const { hits } = await searchProducts('laptop', { filters: 'price < 1000' });
Pattern 3: Error Handling with Algolia Error Types
// src/algolia/errors.ts
import { ApiError } from 'algoliasearch';
export async function safeAlgoliaCall<T>(
operation: string,
fn: () => Promise<T>
): Promise<{ data: T | null; error: string | null }> {
try {
const data = await fn();
return { data, error: null };
} catch (err) {
if (err instanceof ApiError) {
// ApiError has status and message from Algolia API
const msg = `Algolia ${operation} failed [${err.status}]: ${err.message}`;
console.error(msg);
// Specific handling for common codes
if (err.status === 429) {
console.warn('Rate limited — reduce request frequency or contact Algolia');
} else if (err.status === 404) {
console.warn('Index or object not found — verify index name');
}
return { data: null, error: msg };
}
// Non-Algolia error (network, etc.)
const msg = err instanceof Error ? err.message : 'Unknown error';
console.error(`${operation} error: ${msg}`);
return { data: null, error: msg };
}
}
// Usage:
// const { data, error } = await safeAlgoliaCall('search', () =>
// client.searchSingleIndex({ indexName: 'products', searchParams: { query: 'foo' } })
// );
Pattern 4: Batch Operations
// src/algolia/batch.ts
import { getClient } from './client';
// saveObjects handles batching internally — send up to 1000 objects per call
export async function bulkIndex(indexName: string, records: Record<string, any>[]) {
const client = getClient();
const BATCH_SIZE = 1000;
for (let i = 0; i < records.length; i += BATCH_SIZE) {
const batch = records.slice(i, i + BATCH_SIZE);
const { taskID } = await client.saveObjects({
indexName,
objects: batch,
});
await client.waitForTask({ indexName, taskID });
console.log(`Indexed ${Math.min(i + BATCH_SIZE, records.length)}/${records.length}`);
}
}
// Partial update — only send changed fields
export async function updateFields(
indexName: string,
objectID: string,
fields: Record<string, any>
) {
const client = getClient();
return client.partialUpdateObject({
indexName,
objectID,
attributesToUpdate: fields,
});
}
Pattern 5: Multi-Tenant Client Factory
// src/algolia/multi-tenant.ts
import { algoliasearch, type Algoliasearch } from 'algoliasearch';
const tenantClients = new Map<string, Algoliasearch>();
export function getClientForTenant(tenantId: string): Algoliasearch {
if (!tenantClients.has(tenantId)) {
// Each tenant might have their own Algolia app, or use index prefixes
const appId = process.env[`ALGOLIA_APP_ID_${tenantId.toUpperCase()}`]
|| process.env.ALGOLIA_APP_ID!;
const apiKey = process.env[`ALGOLIA_ADMIN_KEY_${tenantId.toUpperCase()}`]
|| process.env.ALGOLIA_ADMIN_KEY!;
tenantClients.set(tenantId, algoliasearch(appId, apiKey));
}
return tenantClients.get(tenantId)!;
}
// Or use a single app with index prefixing
export function tenantIndex(tenantId: string, base: string): string {
return `${tenantId}_${base}`; // "acme_products"
}
Error Handling
| Pattern | Use Case | Benefit |
|---|---|---|
safeAlgoliaCall wrapper | All API calls | Prevents uncaught exceptions, structured error info |
ApiError check | Distinguishing API vs network errors | Targeted retry/recovery logic |
waitForTask | After every write operation | Ensures reads see latest data |
| Batch chunking | Large datasets | Avoids record-too-big and timeout errors |
Resources
Next Steps
Apply patterns in algolia-core-workflow-a for search implementation.
> related_skills --same-repo
> fathom-cost-tuning
Optimize Fathom API usage and plan selection. Trigger with phrases like "fathom cost", "fathom pricing", "fathom plan".
> fathom-core-workflow-b
Sync Fathom meeting data to CRM and build automated follow-up workflows. Use when integrating Fathom with Salesforce, HubSpot, or custom CRMs, or creating automated post-meeting email summaries. Trigger with phrases like "fathom crm sync", "fathom salesforce", "fathom follow-up", "fathom post-meeting workflow".
> fathom-core-workflow-a
Build a meeting analytics pipeline with Fathom transcripts and summaries. Use when extracting insights from meetings, building CRM sync, or creating automated meeting follow-up workflows. Trigger with phrases like "fathom analytics", "fathom meeting pipeline", "fathom transcript analysis", "fathom action items sync".
> fathom-common-errors
Diagnose and fix Fathom API errors including auth failures and missing data. Use when API calls fail, transcripts are empty, or webhooks are not firing. Trigger with phrases like "fathom error", "fathom not working", "fathom api failure", "fix fathom".