Client-side applications
Use the SDK directly in the browser when your application has no server session boundary.
A
JA client-side application is an execution model, not a framework. Use it when the browser renders the application and calls Cavuno directly. Public catalog reads need only a publishable board key; authenticated calls require an explicit browser storage decision.
Create the browser client
Expose only the board key through your build tool:
1VITE_CAVUNO_BOARD=pk_your_32_character_hex_key
123456import { createBoardClient } from '@cavuno/board';export const board = createBoardClient({board: import.meta.env.VITE_CAVUNO_BOARD,auth: { storage: 'session' },});
The SDK connects to https://api.cavuno.com by default. session keeps the login in the current tab. Use memory to clear it on reload. Use local only when cross-tab persistence justifies making the tokens available to JavaScript for longer.
Load public data with cancellation
123456export async function loadJobs(signal?: AbortSignal) {return board.jobs.list({ limit: 20, sort: 'newest' },{ signal },);}
Abort a request when navigation or a newer search makes it irrelevant. An empty data array is a successful empty result; a thrown request belongs in the application’s error state.
Refresh one session at a time
Refresh tokens rotate and are single-use. Share one in-flight refresh across callers:
123456789101112131415161718import { isUnauthorized } from '@cavuno/board';let refreshInFlight: Promise<void> | null = null;export async function withCurrentSession<T>(run: () => Promise<T>) {try {return await run();} catch (error) {if (!isUnauthorized(error)) throw error;refreshInFlight ??= board.auth.refresh().then(() => undefined).finally(() => {refreshInFlight = null;});await refreshInFlight;return run();}}
If refresh fails with 401, stop and show the signed-out state. Never recursively retry a spent refresh token.
Security boundary
- Web Storage tokens are readable by JavaScript. Use a strict content security policy and avoid unsafe HTML injection.
- Browser variables are public. A
pk_…key may be exposed; bearer tokens and Cavuno operator credentials may not. - Gate optional navigation with
board.context().features, while still handling an API denial if configuration changes. - Never put authenticated responses in a shared cache.
Verify the client-side boundary
- Reload after login and confirm
sessionstorage persists only in that tab. - Open a second tab and confirm it does not inherit the login.
- Expire an access token, start parallel authenticated requests, and confirm only one refresh runs.
- Sign out and confirm
board.me.retrieve()produces a typed unauthorized error. - Test the deployed origin in a real browser so CORS and content-security behavior match production.
Prefer a framework-owned httpOnly cookie when your framework has a server boundary. Continue with Authentication and session ownership for the complete model.