Documentation
ReferenceJavaScript SDKStorage

Storage

Where the browser client keeps the session, and how to change it.

The browser client persists the session so a reload does not sign the user out. Which medium it uses is your choice.

Without a storage option it picks Storage.Local when window exists, and an in-memory store of the client's own everywhere else. A client module that is also evaluated during server rendering therefore never touches localStorage, and two clients created on the server never share a session.

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

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

Browser only. createServerClient has no session storage — a server holds no single user's session, so storage and storageKeys are not part of ServerClientOptions.

The adapters

Storage.LocallocalStorageOptional

Survives page reloads and browser restarts.

Storage.SessionsessionStorageOptional

Cleared when the tab or browser closes.

Storage.MemoryMapOptional

Lost on reload. One instance shared by every client that uses it — create a new MemoryStorageAdapter() when each client needs its own.

Storage.CacheCache Storage APIOptional

Survives reloads, and is reachable from service workers.

Choosing between them is a product decision, not a technical one:

  • Local keeps people signed in between visits. What most apps want.
  • Session signs them out when the tab closes. Shared machines, higher-risk products.
  • Memory persists nothing — correct on the server, and in tests where leaking state between cases is worse than re-authenticating.
  • Cache is for when a service worker needs the session too.

Keys

Two entries are written, and both key names can be changed:

createClient(url, key, {
  storageKeys: {
    token: 'my-app-token',
    accounts: 'my-app-accounts',
  },
});

They default to nlv-<slug>-token and nlv-<slug>-token-accounts. The accounts entry only matters with multi-account enabled.

A custom adapter

Any object with these three methods works:

interface NulvlessStorage {
  getItem(key: string): Promise<string | null>;
  setItem(key: string, value: string): Promise<void>;
  removeItem(key: string): Promise<void>;
}

All three are async, so an adapter backed by IndexedDB, a native secure store, or an encrypted wrapper fits without the SDK knowing the difference:

const encrypted: NulvlessStorage = {
  async getItem(key) {
    const raw = await SecureStore.getItemAsync(key);
    return raw ? decrypt(raw) : null;
  },
  async setItem(key, value) {
    await SecureStore.setItemAsync(key, encrypt(value));
  },
  async removeItem(key) {
    await SecureStore.deleteItemAsync(key);
  },
};

createClient(url, key, { storage: encrypted });

On this page