Developing against a remote server
Why a cross-origin backendUrl fails in development, and the proxy configuration that fixes it — for Vite and for Angular.
The examples in this documentation set backendUrl to the Sinequa server. That works when your application is
served from that server, and it is the deployed case. In development it usually is not: your application
runs on localhost and the server is somewhere else — and then it fails.
The fix is a development proxy, and it is not optional.
Why not just configure CORS
Because the session is a cookie, and cookies are harder than headers.
Two obstacles, and CORS only removes the first:
- CORS. The server must name your origin explicitly. With credentials in play,
Access-Control-Allow-Origin: *is rejected by the browser — a wildcard and cookies are mutually exclusive. - The cookie itself. From
localhostto another origin, the session cookie is a third-party cookie. Current browsers restrict those by default, so the requests can succeed while carrying no session at all — which surfaces as a login form that reappears after every successful sign-in.
A proxy sidesteps both: the browser only ever talks to your own origin, so nothing is cross-origin and nothing is third-party.
With a proxy, omit backendUrl
Leave it out and the library builds relative URLs, which the dev server then forwards. Setting it to the remote server defeats the proxy — the browser goes straight there, cross-origin, and you are back to both problems.
What has to be proxied
Not just /api. A search interface reaches the server through several paths, and the ones that are easy to
forget are precisely the ones whose failure is hard to read.
| Path | Why |
|---|---|
/api | Every call the library makes |
/assets | Stylesheets and images a document preview's cached rendition references |
/xdownload | Document downloads and query exports |
/auth/redirect, /saml/redirect | Where an identity provider returns after an OAuth or SAML sign-in |
/r | Server-side redirection links |
/endpoints | Server-declared endpoints |
Miss /assets and previews render unstyled. Miss the redirect paths and a provider sign-in never comes back —
the loop guard fires and reports that the callback is not authorised for this application, which is technically
true and thoroughly misleading.
The configuration
Where these come from
The Angular configuration below is one taken from a working deployment — the path list in particular is what practice settled on, not what a specification says. The Vite variant applies that same list, but it has not been proven the same way: treat it as a starting point, and add whatever your own deployment turns out to reach.
import basicSsl from "@vitejs/plugin-basic-ssl";
import { defineConfig } from "vite";
/** Everything a search interface reaches on the server, not only /api. */
const PROXIED = ["/api", "/assets", "/xdownload", "/auth/redirect", "/saml/redirect", "/r", "/endpoints"];
const target = "https://<your-sinequa-server>";
export default defineConfig({
// HTTPS is required, not a precaution: the session cookie is `Secure`, so it is
// only kept on an https origin. Unlike the Angular CLI, Vite generates no
// certificate — this plugin does. `npm i -D @vitejs/plugin-basic-ssl`
plugins: [basicSsl()],
server: {
proxy: Object.fromEntries(
PROXIED.map((path) => [
path,
{
target,
changeOrigin: true,
// `false` only for a server with a self-signed certificate.
secure: true,
ws: true,
},
]),
),
},
});[
{
"context": [
"/api",
"/xdownload",
"/auth/redirect",
"/saml/redirect",
"/r",
"/endpoints",
"/assets"
],
"target": "https://<your-sinequa-server>",
"secure": true,
"changeOrigin": true,
"ws": true
}
]{
"projects": {
"my-app": {
"architect": {
"serve": {
"options": {
"proxyConfig": "proxy.conf.json",
"ssl": true
}
}
}
}
}
}ssl: true serves the development application over HTTPS, and it belongs with the proxy rather than being
an extra precaution. The session cookie the server issues is marked Secure, and the proxied responses carry it
through to your local origin — so that origin has to be https://localhost, not http://. Without it the
requests succeed and the session quietly does not stick.
The Angular CLI generates a self-signed certificate on its own, which the browser asks you to accept once.
sslKey and sslCert point at your own certificate when you have one.
import { InjectionToken } from "@angular/core";
import { type AtomicClient, createAtomicClient } from "@sinequa/atomic";
export const SINEQUA = new InjectionToken<AtomicClient>("Sinequa client", {
factory: () =>
createAtomicClient({
app: "<your-app>",
// No backendUrl: requests stay relative and the proxy forwards them.
// In production, the application is served by the Sinequa server, so
// relative URLs are correct there too — the same build works in both.
}),
});import basicSsl from "@vitejs/plugin-basic-ssl";
import { defineConfig } from "vite";
/** Everything a search interface reaches on the server, not only /api. */
const PROXIED = ["/api", "/assets", "/xdownload", "/auth/redirect", "/saml/redirect", "/r", "/endpoints"];
const target = "https://<your-sinequa-server>";
export default defineConfig({
// HTTPS is required, not a precaution: the session cookie is `Secure`, so it is
// only kept on an https origin. Unlike the Angular CLI, Vite generates no
// certificate — this plugin does. `npm i -D @vitejs/plugin-basic-ssl`
plugins: [basicSsl()],
server: {
proxy: Object.fromEntries(
PROXIED.map((path) => [
path,
{
target,
changeOrigin: true,
// `false` only for a server with a self-signed certificate.
secure: true,
ws: true,
},
]),
),
},
});Then run the dev server as usual — ng serve, vite — and the application talks only to localhost.
HTTPS is required on both sides
Not only the target: your own dev server has to serve HTTPS. The session cookie is Secure, so it is
dropped on an http:// origin — the requests then succeed while the session silently fails to stick, which
looks exactly like a broken sign-in.
The Angular CLI generates the certificate for you with ssl: true. Vite does not, which is why the
configuration above adds @vitejs/plugin-basic-ssl. Your
browser asks you to accept the certificate once, on first load.
The one build that works everywhere
Omitting backendUrl is not a development-only trick. In production the application is served by the
Sinequa server, so relative URLs are already correct. The same build therefore works in both places, and there
is no environment switch to get wrong.
Keep backendUrl for the cases that genuinely need it: a second backend on one page, a headless consumer with
no origin of its own, or a deployment where CORS is properly configured and the cookie is not an obstacle.