From algolia-pack
Sets up Algolia local dev workflow with env-specific indices, seed scripts, npm/pnpm commands, and Vitest/Jest mocks for isolated testing.
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin algolia-packThis skill is limited to using the following tools:
Set up a fast, reproducible local development workflow for Algolia. Use separate dev indices, mock the client in tests, and iterate without touching production data.
Installs Algolia JavaScript v5 client, configures API keys via env vars, and initializes backend/frontend clients in Node.js/TypeScript projects.
Provides patterns for Algolia search implementation using React InstantSearch hooks, indexing strategies, relevance tuning, and Next.js SSR integration.
Provides expert patterns for Algolia search implementation, indexing strategies, React InstantSearch, relevance tuning, autocomplete, typeahead, and faceted search.
Share bugs, ideas, or general feedback.
Set up a fast, reproducible local development workflow for Algolia. Use separate dev indices, mock the client in tests, and iterate without touching production data.
algolia-install-auth setup// src/algolia/config.ts
import { algoliasearch } from 'algoliasearch';
const ENV = process.env.NODE_ENV || 'development';
// Each environment gets its own index prefix
export function indexName(base: string): string {
if (ENV === 'production') return base;
return `${ENV}_${base}`; // e.g., "development_products"
}
export const client = algoliasearch(
process.env.ALGOLIA_APP_ID!,
process.env.ALGOLIA_ADMIN_KEY!
);
// scripts/seed-algolia.ts
import { client, indexName } from '../src/algolia/config';
const SEED_DATA = [
{ objectID: 'prod-1', name: 'Widget A', category: 'tools', price: 29.99 },
{ objectID: 'prod-2', name: 'Widget B', category: 'tools', price: 49.99 },
{ objectID: 'prod-3', name: 'Gadget C', category: 'electronics', price: 199.99 },
];
async function seed() {
const idx = indexName('products');
// replaceAllObjects atomically swaps index content
const { taskID } = await client.replaceAllObjects({
indexName: idx,
objects: SEED_DATA,
});
await client.waitForTask({ indexName: idx, taskID });
// Configure settings for the dev index
await client.setSettings({
indexName: idx,
indexSettings: {
searchableAttributes: ['name', 'category'],
attributesForFaceting: ['category', 'filterOnly(price)'],
customRanking: ['asc(price)'],
},
});
console.log(`Seeded ${SEED_DATA.length} records into ${idx}`);
}
seed().catch(console.error);
{
"scripts": {
"seed:algolia": "npx tsx scripts/seed-algolia.ts",
"dev": "tsx watch src/index.ts",
"test": "vitest",
"test:watch": "vitest --watch"
}
}
// tests/algolia.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
// Mock the entire algoliasearch module
vi.mock('algoliasearch', () => ({
algoliasearch: vi.fn(() => ({
searchSingleIndex: vi.fn().mockResolvedValue({
hits: [
{ objectID: '1', name: 'Widget A', _highlightResult: {} },
],
nbHits: 1,
page: 0,
nbPages: 1,
}),
saveObjects: vi.fn().mockResolvedValue({ taskID: 123 }),
waitForTask: vi.fn().mockResolvedValue({}),
})),
}));
import { algoliasearch } from 'algoliasearch';
describe('Product Search', () => {
const client = algoliasearch('test-app-id', 'test-api-key');
it('returns matching products', async () => {
const { hits } = await client.searchSingleIndex({
indexName: 'development_products',
searchParams: { query: 'widget' },
});
expect(hits).toHaveLength(1);
expect(hits[0].name).toBe('Widget A');
});
});
// tests/integration/algolia.integration.test.ts
import { describe, it, expect } from 'vitest';
import { algoliasearch } from 'algoliasearch';
describe.skipIf(!process.env.ALGOLIA_APP_ID)('Algolia Integration', () => {
const client = algoliasearch(
process.env.ALGOLIA_APP_ID!,
process.env.ALGOLIA_ADMIN_KEY!
);
const testIndex = `test_${Date.now()}_products`;
it('indexes and searches records', async () => {
// Index
const { taskID } = await client.saveObjects({
indexName: testIndex,
objects: [{ objectID: '1', name: 'Test Product' }],
});
await client.waitForTask({ indexName: testIndex, taskID });
// Search
const { hits } = await client.searchSingleIndex({
indexName: testIndex,
searchParams: { query: 'test' },
});
expect(hits.length).toBeGreaterThan(0);
// Cleanup
await client.deleteIndex({ indexName: testIndex });
});
});
| Error | Cause | Solution |
|---|---|---|
Index does not exist | Dev index not seeded | Run npm run seed:algolia |
| Test pollution | Shared index between tests | Use unique timestamped index names |
| Stale search results | Indexing not waited | Always await client.waitForTask() after writes |
| Mock not applied | Wrong import order | Ensure vi.mock() is before imports |
// scripts/reset-dev-algolia.ts
import { client, indexName } from '../src/algolia/config';
async function reset() {
const idx = indexName('products');
try {
await client.deleteIndex({ indexName: idx });
console.log(`Deleted ${idx}`);
} catch (e) {
// Index may not exist yet — that's fine
}
}
reset().catch(console.error);
See algolia-sdk-patterns for production-ready code patterns.