Skip to content
Skillv1.0.0

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 establishi

by jeremylongshore(0) 0 installs
Free
Sign in to install

Free account. Installing gives you the manifest plus copy-paste snippets.

See reviews

About

Imported from jeremylongshore/tons-of-skills-marketplace (plugins/saas-packs/algolia-pack/skills/algolia-sdk-patterns/SKILL.md). Install upstream with npx skills add jeremylongshore/tons-of-skills-marketplace --skill algolia-sdk-patterns. Copyright stays with the author (MIT).

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

  • algoliasearch v5+ installed
  • Completed algolia-install-auth setup
  • TypeScript project (patterns work in JS too, you just lose type safety)

Instructions

Examples

The client, result, error, batch, and tenant examples below establish a consistent v5 SDK boundary. Centralize credentials and index naming in that boundary so individual callers do not reimplement error or retry policy.

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"
}

Output

The application gains typed, reusable SDK access patterns for search and write paths, with explicit error classification and post-write task waiting. Callers receive structured results without duplicating connection or credential logic.

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.

Use it

Copy one of these into your project. Installing also returns the manifest and these snippets.

yaml
targets:
  - https://api.opensmartroute.ai/api/v1/registry/jeremylongshore-tons-of-skills-marketplace-algolia-sdk-patterns/manifest   # or paste the manifest below

Manifest

An Open Capability Manifest: the router reads it to know what this does, what it costs and when to pick it.

jeremylongshore-tons-of-skills-marketplace-algolia-sdk-patterns.ocm.jsonjson
{
  "ocm": "1",
  "id": "jeremylongshore-tons-of-skills-marketplace-algolia-sdk-patterns",
  "kind": "skill",
  "name": "algolia-sdk-patterns",
  "description": "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\".",
  "publisher": "jeremylongshore",
  "version": "1.0.0",
  "capabilities": {
    "domains": [
      "coding"
    ],
    "tags": [
      "skill-md",
      "saas",
      "search",
      "algolia",
      "skills-sh"
    ],
    "languages": [
      "en"
    ]
  },
  "quality_prior": 0.6,
  "examples": [
    "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\"."
  ],
  "primary": false,
  "metadata": {
    "source": {
      "provider": "skills.sh",
      "repository": "https://github.com/jeremylongshore/tons-of-skills-marketplace",
      "path": "plugins/saas-packs/algolia-pack/skills/algolia-sdk-patterns/SKILL.md",
      "ref": "HEAD",
      "url": "https://github.com/jeremylongshore/tons-of-skills-marketplace/blob/HEAD/plugins/saas-packs/algolia-pack/skills/algolia-sdk-patterns/SKILL.md",
      "key": "jeremylongshore/tons-of-skills-marketplace/plugins/saas-packs/algolia-pack/skills/algolia-sdk-patterns/SKILL.md"
    },
    "compatibility": "Designed for Claude Code",
    "allowed_tools": [
      "Read,",
      "Write,",
      "Edit"
    ],
    "license": "MIT"
  },
  "instructions": "# Algolia SDK Patterns\n\n## Overview\n\nProduction-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.\n\n## Prerequisites\n\n- `algoliasearch` v5+ installed\n- Completed `algolia-install-auth` setup\n- TypeScript project (patterns work in JS too, you just lose type safety)\n\n## Instructions\n\n## Examples\n\nThe client, result, error, batch, and tenant examples below establish a consistent v5 SDK boundary. Centralize credentials and index naming in that boundary so",
  "cost": {
    "context_tokens": 1657
  }
}

Fetch it by URL: GET /api/v1/registry/jeremylongshore-tons-of-skills-marketplace-algolia-sdk-patterns/manifest?version=1.0.0

Reviews

Star ratings from people who tried it. One review per account; edit yours any time.

No reviews yet. Install it, try it, and be the first to rate it.