Panduan Praktis Implementasi Algolia Search dengan Claude Code
Bangun Algolia dengan Claude Code: desain index, key aman, UI, analytics, dan review loop dengan contoh siap pakai.
Tentukan tujuan sebelum membuat search box
Algolia adalah SaaS pencarian yang menyimpan record teroptimasi di search index dan mengembalikan hasil dalam hitungan milidetik. Untuk situs kecil, queryLIKEdi database mungkin cukup. Namun ketika perlu typo tolerance, facet, ranking, synonym, click analytics, dan pencarian multi-bahasa, implementasi manual cepat menjadi berat.
Claude Code berguna karena bisa membaca schema, route, komponen UI, aturan permission, dan model konten sebelum menghasilkan kode. Targetnya bukan hanya search box, tetapi satu alur: bentuk record, index settings, indexing pipeline, secured API key, InstantSearch UI, analytics event, dan relevance review.
Artikel ini memakai Algolia JavaScript API Client v5. Di v5, pola lamainitIndextidak dipakai lagi. Method berada di client dan menerimaindexName, misalnyaclient.saveObjectsdanclient.searchSingleIndex. Lihat juga dokumentasi resmiJavaScript API Client v5, API clients, dan Claude Codecommon workflows.
Tiga use case praktis
Mulai dari use case membuat desain index lebih jelas.
| Use case | Data | Setting penting | Risiko utama |
|---|---|---|---|
| Search dokumentasi | artikel, heading, body, tag | searchableAttributes, synonym, highlight | draft atau catatan internal ikut terindeks |
| Katalog produk atau kursus | nama, kategori, harga, stok, popularitas | facets, customRanking, Insights | harga atau stok tidak sinkron |
| Search knowledge base internal | FAQ, ticket, catatan desain | secured API key, filters, field permission | record privat bocor |
Untuk ClaudeCodeLab, pola ini bisa dipakai untuk search blog publik, materi training, dan template. Sebelum UI, tentukan siapa yang boleh melihat apa, atribut apa yang memengaruhi ranking, dan query mana yang harus mengarah ke training, template, atau konsultasi.
Record search harus minimal dan aman
Jangan copy seluruh row database ke Algolia. Index hanya field publik yang perlu ditampilkan dan metadata minimum untuk ranking serta filter. Email, payment ID, memo internal, konten belum terbit, dan raw API response tidak boleh masuk index.
{
"objectID": "article_id_claude-code-algolia-search",
"title": "Panduan Praktis Implementasi Algolia Search dengan Claude Code",
"summary": "Panduan desain index, UI, analytics, dan review loop",
"content": "Teks searchable yang diekstrak hanya dari konten terbit",
"locale": "id",
"section": "blog",
"category": "use-cases",
"tags": ["Claude Code", "Algolia", "search"],
"visibility": "public",
"allowedTeams": [],
"slug": "claude-code-algolia-search",
"url": "/id/blog/claude-code-algolia-search",
"publishedAt": "2025-11-15",
"updatedAt": "2026-06-01",
"updatedAtTimestamp": 1780272000,
"popularity": 42,
"conversionScore": 7,
"readingMinutes": 12,
"thumbnail": "/images/hero/hero-090.png"
}
objectIDharus stabil. Jika ID berubah setiap title atau URL berubah, history analytics dan tuning relevance akan terputus. Untuk artikel, gunakan polaarticle_locale_slug; untuk produk, gunakanproduct_databaseId.
Script indexing dengan Algolia v5
Install dependency lebih dulu.
npm install algoliasearch@5 dotenv
SimpanALGOLIA_APP_ID, ALGOLIA_ADMIN_KEY, dan opsionalALGOLIA_INDEX_NAMEdi.env. Admin key hanya boleh dipakai di server.
// scripts/index-articles.ts
import "dotenv/config";
import { algoliasearch } from "algoliasearch";
type SearchRecord = {
objectID: string;
title: string;
summary: string;
content: string;
locale: "id" | "en";
section: "blog" | "docs" | "product";
category: string;
tags: string[];
visibility: "public" | "restricted";
allowedTeams: string[];
slug: string;
url: string;
publishedAt: string;
updatedAt: string;
updatedAtTimestamp: number;
popularity: number;
conversionScore: number;
readingMinutes: number;
thumbnail: string;
};
const appId = process.env.ALGOLIA_APP_ID;
const adminKey = process.env.ALGOLIA_ADMIN_KEY;
const indexName = process.env.ALGOLIA_INDEX_NAME ?? "claudecodelab_articles";
if (!appId || !adminKey) {
throw new Error("ALGOLIA_APP_ID and ALGOLIA_ADMIN_KEY are required");
}
const client = algoliasearch(appId, adminKey);
const records: SearchRecord[] = [
{
objectID: "article_id_claude-code-algolia-search",
title: "Panduan Praktis Implementasi Algolia Search dengan Claude Code",
summary: "Panduan desain index, UI, analytics, dan review loop",
content: "Index hanya teks searchable dari konten yang sudah terbit.",
locale: "id",
section: "blog",
category: "use-cases",
tags: ["Claude Code", "Algolia", "search"],
visibility: "public",
allowedTeams: [],
slug: "claude-code-algolia-search",
url: "/id/blog/claude-code-algolia-search",
publishedAt: "2025-11-15",
updatedAt: "2026-06-01",
updatedAtTimestamp: 1780272000,
popularity: 42,
conversionScore: 7,
readingMinutes: 12,
thumbnail: "/images/hero/hero-090.png"
}
];
await client.setSettings({
indexName,
indexSettings: {
searchableAttributes: [
"unordered(title)",
"unordered(summary)",
"content",
"tags",
"category"
],
attributesForFaceting: [
"filterOnly(visibility)",
"filterOnly(locale)",
"filterOnly(allowedTeams)",
"searchable(category)",
"searchable(tags)",
"section"
],
customRanking: [
"desc(conversionScore)",
"desc(popularity)",
"desc(updatedAtTimestamp)"
],
attributesToRetrieve: [
"title",
"summary",
"locale",
"section",
"category",
"tags",
"url",
"updatedAt",
"thumbnail"
],
attributesToHighlight: ["title", "summary", "content"],
typoTolerance: true,
removeWordsIfNoResults: "lastWords"
}
});
await client.saveSynonyms({
indexName,
synonymHit: [
{
objectID: "claude-code-names",
type: "synonym",
synonyms: ["Claude Code", "claude code", "kode Claude"]
},
{
objectID: "search-id",
type: "synonym",
synonyms: ["search", "pencarian", "site search", "full-text search"]
}
],
clearExistingSynonyms: true
});
const { taskID } = await client.saveObjects({
indexName,
objects: records
});
await client.waitForTask({ indexName, taskID });
console.log(`Indexed ${records.length} records into ${indexName}`);
Urutan disearchableAttributesmenentukan prioritas. Field permission sebaiknya memakaifilterOnly, sehingga bisa membatasi hasil tanpa muncul sebagai facet publik.
Search endpoint dan secured API key
Browser boleh memakai search-only key untuk public search. Admin key dan key dengan permission tulis tidak boleh masuk frontend. Jika hasil harus dibatasi per user atau team, buat secured API key di server. Panduan resmiAPI keysmenjelaskan konsep ini.
// app/api/search-key/route.ts
import { algoliasearch } from "algoliasearch";
import { NextResponse } from "next/server";
const appId = process.env.ALGOLIA_APP_ID!;
const searchKey = process.env.ALGOLIA_SEARCH_KEY!;
const indexName = process.env.ALGOLIA_INDEX_NAME ?? "claudecodelab_articles";
export async function GET() {
const user = { id: "user_123", teamIds: ["training"] };
const client = algoliasearch(appId, searchKey);
const securedApiKey = client.generateSecuredApiKey({
parentApiKey: searchKey,
restrictions: {
restrictIndices: indexName,
filters: `visibility:public OR allowedTeams:${user.teamIds[0]}`,
userToken: user.id,
validUntil: Math.floor(Date.now() / 1000) + 60 * 30
}
});
return NextResponse.json({ appId, indexName, apiKey: securedApiKey });
}
Jika ingin semua search lewat server, batasi input dan return hanya field aman. Referensi method ada diSearch an index.
// app/api/search/route.ts
import { algoliasearch } from "algoliasearch";
import { NextRequest, NextResponse } from "next/server";
const client = algoliasearch(
process.env.ALGOLIA_APP_ID!,
process.env.ALGOLIA_SEARCH_KEY!
);
const indexName = process.env.ALGOLIA_INDEX_NAME ?? "claudecodelab_articles";
export async function GET(request: NextRequest) {
const query = request.nextUrl.searchParams.get("q")?.slice(0, 80) ?? "";
const locale = request.nextUrl.searchParams.get("locale") ?? "id";
const result = await client.searchSingleIndex({
indexName,
searchParams: {
query,
filters: `visibility:public AND locale:${locale}`,
hitsPerPage: 10,
attributesToRetrieve: ["title", "summary", "url", "category", "tags"],
clickAnalytics: true
}
});
return NextResponse.json({
hits: result.hits,
queryID: result.queryID,
nbHits: result.nbHits
});
}
UI dengan InstantSearch
InstantSearch.jsmenyediakan widget untuk search box, facet, highlight, statistik, dan pagination.
// components/ArticleSearch.tsx
"use client";
import { liteClient as algoliasearch } from "algoliasearch/lite";
import {
Configure,
Highlight,
Hits,
InstantSearch,
Pagination,
RefinementList,
SearchBox,
Stats
} from "react-instantsearch";
type HitProps = {
hit: {
objectID: string;
title: string;
summary: string;
url: string;
category: string;
tags: string[];
updatedAt: string;
};
};
const searchClient = algoliasearch(
process.env.NEXT_PUBLIC_ALGOLIA_APP_ID!,
process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY!
);
function HitCard({ hit }: HitProps) {
return (
<article className="rounded border p-4">
<a href={hit.url} className="font-bold">
<Highlight attribute="title" hit={hit} />
</a>
<p className="mt-2 text-sm text-gray-600">
<Highlight attribute="summary" hit={hit} />
</p>
<p className="mt-2 text-xs text-gray-500">
{hit.category} · {hit.updatedAt}
</p>
</article>
);
}
export function ArticleSearch() {
return (
<InstantSearch searchClient={searchClient} indexName="claudecodelab_articles">
<Configure
hitsPerPage={8}
filters="visibility:public AND locale:id"
clickAnalytics
/>
<SearchBox placeholder="Cari artikel Claude Code" />
<Stats />
<div className="mt-6 grid gap-6 md:grid-cols-[220px_1fr]">
<aside>
<h2 className="text-sm font-bold">Kategori</h2>
<RefinementList attribute="category" searchable />
<h2 className="mt-4 text-sm font-bold">Tag</h2>
<RefinementList attribute="tags" searchable />
</aside>
<main>
<Hits hitComponent={HitCard} />
<Pagination className="mt-6" />
</main>
</div>
</InstantSearch>
);
}
Analytics dan review loop
Search yang bagus dibangun lewat iterasi: query, zero-result search, posisi klik, conversion, lalu perubahan terarah pada record, settings, synonym, dan UI.
// lib/search-insights.ts
import aa from "search-insights";
aa("init", {
appId: process.env.NEXT_PUBLIC_ALGOLIA_APP_ID!,
apiKey: process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY!,
useCookie: true
});
export function trackSearchClick(params: {
indexName: string;
objectID: string;
queryID: string;
position: number;
}) {
aa("clickedObjectIDsAfterSearch", {
eventName: "Article Clicked",
index: params.indexName,
queryID: params.queryID,
objectIDs: [params.objectID],
positions: [params.position]
});
}
Gunakan Claude Code sebagai reviewer dengan format jelas.
You are the search quality reviewer for ClaudeCodeLab.
Review Algolia queries, zero-result searches, top 10 results, CTR, and conversions.
Output:
| query | problem | cause | proposed change | risk | priority |
Rules:
- Do not add private fields to the index.
- Separate changes into settings, synonyms, record content, and UI.
- Check whether the expected article appears in the top 3.
- Decide whether a synonym, title rewrite, body edit, or facet change is best.
- Check whether training, templates, and consultation CTAs match search intent.
Kesalahan yang sering terjadi
Pertama, mengekspos key yang salah. Semua environment variable denganNEXT_PUBLIC_akan masuk browser. Di sana hanya boleh ada search-only key atau secured API key dari server.
Kedua, mengindex terlalu banyak field. Jika field privat masuk Algolia, anggap field itu bisa diambil. Bersihkan record sebelum indexing dan sempitkanattributesToRetrieve.
Ketiga, ranking berdasarkan intuisi saja. Mulai dari title dan summary, lalu gunakanconversionScore, popularity, dan freshness sebagai tie-breaker. Review dengan Insights setiap minggu.
Keempat, synonym terlalu luas. Menghubungkan “AI”, “Claude”, dan “ChatGPT” tanpa bukti membuat intent kabur. Tambahkan synonym ketika log menunjukkan zero-result atau variasi istilah yang nyata.
Kelima, testing sebelum task selesai. Setelah settings, synonym, atau record disimpan, tungguwaitForTask.
Hubungkan dengan monetisasi
Search juga bagian dari funnel. Query “Algolia search” harus mengarah ke panduan ini; “CLAUDE.md template” harus menujutemplate CLAUDE.md; pertanyaan tentang rollout tim bisa diarahkan keClaudeCodeLab consultation. Tambahkan jugasearch functionality guidedanperformance optimization.
ClaudeCodeLab dapat membantu training Claude Code, prompt templates, CLAUDE.md templates, dan konsultasi implementasi. Sebelum menulis kode, daftar field publik, metrik ranking, dan query dengan intent komersial.
Ringkasan
Claude Code dan Algolia paling efektif saat search diperlakukan sebagai product loop: record aman, key terpisah, ranking jelas, indexing sinkron, UI berguna, analytics, dan review rutin.
Setelah mencoba alur di artikel ini, pengurangan rework terbesar datang dari membatasi field record danattributesToRetrievesejak awal. Prompt review Claude Code juga memudahkan zero-result fix, synonym, perubahan konten, serta CTA training, template, dan consultation ditinjau dalam satu rutinitas mingguan.
PDF gratis: cheatsheet Claude Code
Masukkan email dan unduh satu halaman berisi command, kebiasaan review, dan workflow aman.
Kami menjaga datamu dan tidak mengirim spam.
Tentang penulis
Masa
Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.
Artikel terkait
Workflow Obsidian ke CLAUDE.md untuk Claude Code
Ubah catatan kerja Obsidian menjadi operating note CLAUDE.md agar konteks tidak dijelaskan ulang.
Claude Code Revenue CTA Routing: dari artikel ke PDF, Gumroad, dan konsultasi
Workflow Claude Code untuk mengarahkan pembaca ke PDF gratis, Gumroad, atau konsultasi sesuai intent.
Aturan handoff tim Claude Code: bukti review, permission, rollback, dan jalur revenue
Format handoff Claude Code untuk tim: bukti, permission rule, rollback, PDF gratis, Gumroad, dan konsultasi.