Claude-code-plugins-plus algolia-local-dev-loop
install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/algolia-pack/skills/algolia-local-dev-loop" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-algolia-local-dev-loop && rm -rf "$T"
manifest:
plugins/saas-packs/algolia-pack/skills/algolia-local-dev-loop/SKILL.mdsource content
Algolia Local Dev Loop
Overview
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.
Prerequisites
- Completed
setupalgolia-install-auth - Node.js 18+ with npm/pnpm
- Vitest or Jest for testing
Instructions
Step 1: Environment-Based Index Names
// 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! );
Step 2: Seed Script for Dev Data
// 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" } }
Step 3: Mock Algolia in Unit Tests
// 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'); }); });
Step 4: Integration Test with Real API
// 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 Handling
| Error | Cause | Solution |
|---|---|---|
| Dev index not seeded | Run |
| Test pollution | Shared index between tests | Use unique timestamped index names |
| Stale search results | Indexing not waited | Always after writes |
| Mock not applied | Wrong import order | Ensure is before imports |
Examples
Clean Dev Index on Start
// 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);
Resources
Next Steps
See
algolia-sdk-patterns for production-ready code patterns.