@sinequa/atomic

URL state

Making a search shareable and navigable through the address bar — with the helpers' short keys, and their rough edges.

A search that lives only in memory cannot be shared, bookmarked, or reached with the back button. Putting it in the URL fixes all three, and the library ships helpers for the encoding.

These helpers predate the current filter model

QueryParams.filters is typed LegacyFilter[], not Filter. So round-tripping through these helpers converts your filters to the legacy shape, and you convert back with legacyFilterToFilter. For new applications, serialising your own state is often cleaner — the last section shows both.

The short keys

The encoding uses one- or two-letter parameters, so a shared URL stays readable:

KeyHolds
qThe search text
fThe filters, as encoded JSON
pThe page
sThe sort choice name
tThe selected tab
nThe query web service name
bThe basket
idThe opened document

Prop

Type

Read the URL on load, write it on change
src/url-state.ts
import {
  createAtomicClient,
  filter,
  getQueryParamsFromUrl,
  getUrlParamsFromQueryParams,
  legacyFilterToFilter,
} from "@sinequa/atomic";
import type { Filter, Query, QueryParams } from "@sinequa/atomic";

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

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

/** The URL is the source of truth: read it, search, render. */
export async function searchFromUrl() {
  const params = getQueryParamsFromUrl(window.location.href) ?? { filters: [] };

  const query: Query = {
    name: params.name ?? "<your-query>",
    text: params.text,
    // Falsy on a missing parameter, and `page` is one-based — see the pitfalls.
    page: params.page || 1,
    pageSize: 10,
    tab: params.tab,
    sort: params.sort,
    // The helpers hand back the legacy shape; convert it to the current model.
    filters: fromLegacy(params.filters),
  };

  return client.api.query.search(query);
}

/** Push the state into the URL without reloading the page. */
export function writeUrl(params: QueryParams) {
  const search = getUrlParamsFromQueryParams(params);
  history.pushState(null, "", `${window.location.pathname}?${search}`);
}

function fromLegacy(legacy: QueryParams["filters"]): Filter | undefined {
  return filter.and(...(legacy ?? []).map((one) => legacyFilterToFilter(one)));
}

// Back and forward must re-run the search, or the view and the URL disagree.
window.addEventListener("popstate", () => {
  void searchFromUrl();
});

Pitfalls

What's next

On this page