Use Indx from the Server (Next.js, SSR, any backend)

The HTTP API is plain REST over fetch. Nothing about it needs a browser, so it works from a Node server, a Next.js Route Handler or Server Component, an edge function, or any backend. The API key stays on the server and never reaches the client.

Packages

  • @indxsearch/indx-types — TypeScript types for every request and response. Types only, no runtime: safe in server components, edge runtimes and client bundles alike.
  • @indxsearch/intrface — React client components for the interactive search UI (input, results, facets, sorting). Use it for the parts that respond to typing.

Server-rendered search page (Next.js App Router)

ts
// app/search/page.tsx — a Server Component
import type { QueryProxy, Result } from '@indxsearch/indx-types';

const BASE = `${process.env.INDX_URL}/api/teams/${process.env.INDX_TEAM}/datasets/${process.env.INDX_DATASET}`;
const headers = { 'Content-Type': 'application/json', Authorization: `Bearer ${process.env.INDX_TOKEN}` };

export default async function SearchPage({ searchParams }: { searchParams: { q?: string } }) {
  const query: QueryProxy = { text: searchParams.q ?? '', maxNumberOfRecordsToReturn: 20, enableFacets: true };
  const result: Result = await fetch(`${BASE}/search`, { method: 'POST', headers, body: JSON.stringify(query), cache: 'no-store' })
    .then(r => r.json());
  const docs = await fetch(`${BASE}/documents/lookup`, { method: 'POST', headers, body: JSON.stringify(result.records.map(r => r.documentKey)) })
    .then(r => r.json());
  return <ResultList docs={docs} facets={result.facets} />;
}

The first page renders on the server with the full result and facet counts, so it is crawlable and needs no client JavaScript to show results. Hand over to @indxsearch/intrface on the client for search-as-you-type; it calls the same endpoints with a token you issue for the browser — a separate key with the access level Search only, limited to the datasets the page searches. Every visitor can read that key, so it must not be able to do anything else.

Caching

Responses are deterministic for a given request body and index build. Cache them in your framework's data cache or a CDN as you would any API response — the empty (browse) search is the usual candidate — and invalidate when documents change or the dataset is reloaded.

Timeouts and cancellation

Set timeOutLimitMilliseconds in the query to bound scoring time (the response then carries didTimeOut), and pass an AbortSignal to fetch for a hard client-side cancel.