@sinequa/atomic

The HTTP layer

Calling an endpoint that has no typed wrapper, and the six per-request options every call accepts.

Most of the time you use client.api.*, which is typed and does the post-processing. The layer underneath is there for what it does not cover: a plugin endpoint, an API newer than the library, a request that needs an abort signal.

the five verbs
await client.http.get<T>(endpoint, params?, options?);
await client.http.post<T>(endpoint, body, options?);
await client.http.put<T>(endpoint, body, options?);
await client.http.patch<T>(endpoint, body, options?);
await client.http.del<T>(endpoint, params?, options?);

Every one of them adds the headers, the body fields and the error handling described below — so a raw call is still an authenticated, audited, recoverable call.

What a request carries for you

Whichever verb you use, the library adds:

Sinequa-csrf-tokenThe stored session token, when there is one
Authorization: BearerIn bearer mode — unless a transport is configured, which attaches its own
sinequa-override-user · -domainWhile impersonation is active
Sinequa-Force-Camel-CaseSo the response keys are camel-cased
appIn the body of every POST/PUT/PATCH/DELETE
locale · ui-languageFrom uiLanguage, falling back to the browser's language
noUserOverrideThe negation of userOverrideActive
noAutoAuthenticationResolved from the auth mode — false only in sso

And on the way back: the token is refreshed from the sinequa-jwt-refresh header, a 401 may trigger recovery, and any failure emits requestFailed before throwing.

Calling an endpoint with no wrapper

A custom plugin endpoint
src/plugin.ts
import { createAtomicClient } from "@sinequa/atomic";

const client = createAtomicClient({
  app: "<your-app>",
  backendUrl: "https://<your-sinequa-server>",
});

await client.initializeConfig();
await client.auth.login();

/** The shape your plugin answers with — you declare it, nothing validates it. */
type Summary = { text: string; tokens: number };

// An endpoint starting with `plugin` gets the `api/v1/` prefix automatically.
const summary = await client.http.post<Summary>("plugin/summarize", {
  documentId: "abc123",
  maxTokens: 200,
});

console.log(summary.text);

API_ENDPOINTS autocompletes the 32 known paths while still accepting any string, so a plugin or a new endpoint needs no cast.

Request options

Prop

Type

A timeout for one request

There is no timeout option: ordinary requests wait as long as the network does. createTimeoutController gives you a signal that aborts:

five seconds, then give up
import { createTimeoutController, TimeoutError } from "@sinequa/atomic";

const { signal } = createTimeoutController(5_000);

try {
  await client.http.post("api/v1/query", { name: "<your-query>", text }, { signal });
} catch (error) {
  if (error instanceof TimeoutError) notify("The server is not responding.");
  else throw error;
}

Only the authentication layer has a timeout of its own — auth.timeoutMs, ten seconds by default.

Getting the raw response

responseType: "response" is what an export or a download needs:

reading headers and body
const response = await client.http.get<Response>("api/v1/…", undefined, {
  responseType: "response",
});

const name = /filename="?([^"]+)"?/.exec(response.headers.get("content-disposition") ?? "")?.[1];
const blob = await response.blob();

Pitfalls

What's next

On this page