Documentation
ReferenceJavaScript SDKClients

Clients

createClient for the browser, createServerClient for everything else.

createClient — browser

import { createClient, Storage } from '@nulvless/nulvless-js';

const nulvless = createClient(url, publishableKey, options?);
urlstringOptional

Your environment's issuer — https://<project>.nulvless.co. In production you can use your custom domain instead, such as https://api.example.com.

publishableKeystringOptional

Publishable key, starting with pk-.

optionsClientOptionsOptional

Optional — see Configuration.

lib/nulvless.ts
export const nulvless = createClient('https://acme.nulvless.co', 'pk-…', {
  storage: Storage.Local,
  trackUser: true,
  language: () => 'pt-BR',
});

The publishable key is safe to ship. It can only do what the signed-in user could already do — it is not a capability of its own.

Session storage

The browser client persists the session for you — in localStorage by default, or, when the code runs where there is no window (SSR, tests), in an in-memory store of the client's own. Pick another adapter with storage:

import { Storage } from '@nulvless/nulvless-js';

createClient(url, key, { storage: Storage.Local });

Keys default to nlv-<slug>-token and nlv-<slug>-token-accounts, and can be overridden with storageKeys.

createServerClient — server

import { createServerClient } from '@nulvless/nulvless-js';

const nulvless = createServerClient(url, secretKey, options?);
urlstringOptional

Your environment's issuer

secretKeystringOptional

Secret key, starting with sk-

optionsServerClientOptionsOptional

Same as ClientOptions minus storage and storageKeys

Never ship a secret key to the browser

sk- keys reach the administrative parts of the API. Create the server client only in server code — API routes, handlers, workers.

Forwarding the user's headers

Session fingerprinting records the device and location a session was created from. On the server, the request the platform sees is your server's, not the user's — so those fields would describe your infrastructure.

forwardUserHeaders copies the relevant headers from the incoming request:

import { createServerClient, forwardUserHeaders } from '@nulvless/nulvless-js';

const nulvless = createServerClient(url, 'sk-…');

const { data, error } = await nulvless.auth.grants.invite(
  { invites: [{ email: '[email protected]', role_id: roleId }], resource: 'acme' },
  { headers: forwardUserHeaders(req) },
);

To apply it to every call instead of per call, resolve headers lazily:

createServerClient(url, key, {
  trackUser: true,
  headers: () => forwardUserHeaders(cls.get('req')),
});

Keeping auth responses out of caches

forwardUserHeaders handles the request on the way in. This handles your response on the way out.

Whenever your own response sets, rotates or clears a session, apply applyAuthCacheHeaders to it:

import { createServerClient, forwardUserHeaders, applyAuthCacheHeaders } from '@nulvless/nulvless-js';

export async function POST(request: Request) {
  const { data } = await nulvless.auth.signIn(params, {
    headers: forwardUserHeaders(request),
  });

  const response = NextResponse.json({ ok: true });
  response.cookies.set('session', data.access_token);

  return applyAuthCacheHeaders(response);
}

It takes a Web Response (App Router, Workers, Deno) or a Node ServerResponse (Express, Pages Router), and returns it so the call can be inlined into a return:

app.post('/login', async (req, res) => {
  const { data } = await nulvless.auth.signIn(params, {
    headers: forwardUserHeaders(req),
  });

  applyAuthCacheHeaders(res);
  res.cookie('session', data.access_token, { httpOnly: true, sameSite: 'lax' });
  res.json({ ok: true });
});

Where a framework takes a plain header map instead, spread AUTH_CACHE_HEADERS.

A cached sign-in response hands the session to the next visitor

If a CDN caches a response carrying a fresh session cookie, it serves that cookie to the next visitor. The protection you would expect does not apply: shared caches are barred from reusing responses to requests carrying Authorization, but yours is authenticated by cookie, which carries no such rule. This has bitten production apps on CloudFront, Vercel Edge and Cloudflare.

Responses from the nulvless API already carry Cache-Control: no-store, so this is only about responses your own app returns.

Multi-account

Set multiUser: true on a browser client to keep several signed-in accounts and switch between them.

const nulvless = createClient(url, key, { multiUser: true });

await nulvless.auth.add(authenticated);
const accounts = await nulvless.auth.list();
await nulvless.auth.switch(userId);
await nulvless.auth.remove(userId);

auth.get() returns the active session and auth.set() replaces it. This is off by default — enable it only if your product actually switches accounts.

On this page