@sinequa/atomic

The client

Create a client, understand what it owns, and know when the free-function form is the right one instead.

Everything this library does goes through a client: it holds the configuration, owns the session, carries the transport, and exposes the typed API. Create one, keep it, pass it around.

Marked @experimental

createAtomicClient and its type carry an @experimental tag in the source: "the shape may still change before it replaces the free functions". It is nonetheless the form this documentation teaches, because it is the one being moved towards and the only one that supports more than one backend. The free functions are stable and remain fully supported.

Creating one

One client for the application
src/sinequa.ts
import { createAtomicClient } from "@sinequa/atomic";

/**
 * Module scope, so the whole application shares one configuration and one session.
 * Creating a client is inert: no request is made until you ask for one.
 */
export const sinequa = createAtomicClient({
  app: "<your-app>",
  backendUrl: "https://<your-sinequa-server>",
});

/** Started once; anything that queries awaits it. */
export const ready = (async () => {
  await sinequa.initializeConfig();
  await sinequa.auth.login();
})();

What a client owns

client configlive view authsession + events httpget · post · put · patch · del api13 typed namespaces Web StoragestoragePrefix transportfetch, or an adapter

Prop

Type

The typed API

client.api groups the endpoints by what they act on. This is the canonical way to call them:

the shape of client.api
client.api.query.search(query);        // POST api/v1/query
client.api.query.aggregate(agg, query); // paging through one aggregation's items
client.api.app.get();                   // the server configuration
client.api.principal.get();             // the current user
client.api.preview.get(id, query);      // a document's preview
client.api.labels.add(labels, ids);     // tag documents
client.api.userSettings.patch(settings);

Each method is the matching free function, already bound to this client. Nothing else differs — same parameters, same return types, minus the trailing context argument.

The free-function form

Every call also exists as a module-level function:

the same two calls
import { fetchQuery, setGlobalConfig } from "@sinequa/atomic";

setGlobalConfig({ app: "<your-app>", backendUrl: "https://<your-sinequa-server>" });
const result = await fetchQuery({ name: "<your-query>", text: "report" });

These run on a default client whose configuration is globalConfig, read live. getDefaultClient() returns that same instance, so the two forms are two views of one state and can be mixed freely.

Reach for the free functions when a client cannot be handed to you — a distributed web component, a plugin, a utility module deep in a call stack. Prefer an explicit client everywhere else: it is what makes a second backend, and testing without module mocks, possible at all.

A third form exists, and should not be used

API.principal.get() — a legacy aggregate object — is a third spelling of the same call, kept for compatibility. It was never extended beyond principal, so it covers one endpoint family out of twelve: learning it teaches you a pattern that does not apply anywhere else. client.api covers all of them.

Configuration read live

client.config is a view, not a snapshot. That matters after initializeConfig(), which is what resolves the values the server decides:

Reading what was resolved
src/report-config.ts
import { createAtomicClient, isExternalAuthMode } from "@sinequa/atomic";

const client = createAtomicClient({ app: "<your-app>" });

// Before: backendUrl and the auth mode are not settled yet.
console.log(client.config.authMode?.kind); // 'unknown'

await client.initializeConfig();

// After: derived from the page URL and from the server's pre-login response.
console.log(client.config.backendUrl);
console.log(client.config.authMode?.kind); // 'sso' | 'oauth' | 'saml' | 'bearer' | 'credentials'

// Which decides whether a sign-in screen shows a form or a loader.
console.log(isExternalAuthMode(client.config.authMode) ? "loader" : "credentials form");

Pitfalls

What's next

On this page