SDKs

Server SDK

@findlane/server uploads and updates catalogs, changes settings, and searches with a workspace API key. It runs in Node.js 18+, Bun, Deno, and edge runtimes.

Install

npm install @findlane/server

Create a client

import { SearchServerClient } from "@findlane/server"

const client = new SearchServerClient({
  workspaceId: process.env.FINDLANE_WORKSPACE_ID!,
  apiKey: process.env.FINDLANE_API_KEY!,
})

const products = client.index<Product>("products")
Option
workspaceIdThe workspace ID, from the API keys or Settings page.
apiKeyA workspace API key. Keep it on your server.
baseUrlThe API to call. Leave it out to use Findlane's hosted API.
fetchA fetch implementation to use instead of the global one.

client.index<T>(name) returns the methods for one index. T types your records, which each need a string objectID. Every method returns a promise and takes an optional AbortSignal.

MethodScope
search(request, signal?)searchSearch the index. Takes the same request as the browser SDK, with up to 100 hits.

Records

MethodScope
upsert(records, signal?)writeAdd or replace up to 1,000 records by objectID.
delete(objectID, signal?)writeDelete a record.
getRecord(objectID, signal?)eitherA record and its version; record is null if it doesn't exist.
upsertOne(record, options)writeWrite one record if it's still at options.expectedVersion.
deleteOne(objectID, options)writeDelete one record if it's still at options.expectedVersion.
replaceAll(records, signal?)writeReplace the catalog in one request, up to 100,000 records.
replaceAllStaged(records, options?)writeReplace the catalog in batches, switched in one commit.
abortReplacement(stageId, signal?)writeDrop an unfinished staged replacement.

Writes return a receipt: { acceptedRevision, indexedRevision, recordCount }. See Records for how to use them.

replaceAllStaged takes an array or an async iterable of records, and these options:

Option
stageIdNames the stage, so a retry continues it. Default: a random ID.
batchSizeRecords per batch, 1 to 1,000. Default 500.
maxBatchBytesBytes per batch. Default 8 MiB.
signalCancels the upload.

upsertOne and deleteOne take { expectedVersion, idempotencyKey?, signal? }. The lower-level beginReplacement, uploadReplacementBatch, and commitReplacement are also available for running the stage steps yourself.

Settings

MethodScope
getSettings(signal?)eitherThe index's settings, and a settings change in progress in rebuild.
replaceSettings(settings, signal?)writeSave the full settings. applied says how they took effect.
waitForSettings(options?)eitherWait until a background settings change finishes; throws if it failed.

See Search settings for the fields and how changes apply.

Indexing status

MethodScope
getIndexingStatus(signal?)either{ acceptedRevision, indexedRevision }.
waitUntilIndexed(revision, options?)eitherWait until a revision is searchable.

Writes are searchable when they return, so waitUntilIndexed returns at once. It's there so code written for search services that index later keeps working.

Browser search access

MethodScope
getPublicSearchAccess(signal?)writeThe public ID, allowed origins, and policy.
setPublicSearchPolicy(policy, signal?)writeLimit what browser search can read and query.
usePublicIndexDefaults(signal?)writeRemove the policy.

Allowed origins are changed in the dashboard. See Security.

Errors

A failed request throws a FindlaneError with the HTTP status and the API's message. replaceAllStaged throws a StagedReplacementError with the stageId to retry or abort, and the original error as its cause.

import { FindlaneError } from "@findlane/server"

try {
  await products.upsert(records)
} catch (error) {
  if (error instanceof FindlaneError && error.status === 402) alertTeam(error.message)
  else throw error
}

The staged replacement and conditional write methods retry network errors and 408, 429, 502, 503, and 504 responses up to three times. See the error codes.