Clients
createClient for the browser, createServerClient for everything else.
createClient — browser
import { createClient, Storage } from '@nulvless/nulvless-js';
const nulvless = createClient(url, publishableKey, options?);urlstringOptionalYour environment's issuer — https://<project>.nulvless.co. In production you
can use your custom domain instead, such as
https://api.example.com.
publishableKeystringOptionalPublishable key, starting with pk-.
optionsClientOptionsOptionalOptional — see Configuration.
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?);urlstringOptionalYour environment's issuer
secretKeystringOptionalSecret key, starting with sk-
optionsServerClientOptionsOptionalSame 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.