Tools
The Ulabase Kit
The client side of your backend: sign-up, sign-in, sessions, teams, invitations and payments for React, Angular, Vue, Next.js and Nuxt. One package.
The Ulabase Kit is the client code that talks to your service: sign-up, sign-in, email verification, password reset, teams and invitations, and, for an app that sells something, subscriptions and orders.
npm install @ulabase/kit
import { checkSession, login, logout } from '@ulabase/kit';
const config = { apiBaseUrl: 'https://c0ffee.ulabase.app' };
const user = await checkSession(config); // UserInfo, or null
await login(config, 'user@example.com', 'secret');
await logout(config);
Every function takes the same config as its first argument. There is nothing to initialise or
register, and no state in a module global.
What it saves you
Sign-up, sessions and checkout each take several steps.
Signing up is a POST, an email, a verification link that lands on a different origin, a redirect
carrying a token in a URL fragment, and a token to store. A session is a JWT that expires in
fifteen minutes and has to be renewed before it does, from a timer that survives a backgrounded
tab. A checkout is a redirect to Stripe, a return to a page that knows which order came back, and a
webhook that may arrive after the customer does.
The kit handles those flows against your service’s sign-up and Stripe features. It has no dependencies of its own and works with any framework.
The packages
| Package | What it adds |
|---|---|
|
The core. Plain TypeScript, zero dependencies, works with any framework or none. Everything below is built on it. |
|
Angular: |
|
React: |
|
Vue: |
Each adapter wraps the core: the same functions, exposed the way that framework expects state to
arrive. Payments have their own API, apart from auth: a subscription loads on sign-in, reloads when
the user switches team, and stays untouched when the service has no stripe feature.
Each adapter installs the core as a dependency. Each package’s README has the exact line.
Setting it up
Angular, in app.config.ts:
providers: [provideRhAuth({ apiBaseUrl: environment.apiUrl })]
React, near the root:
<RhAuthProvider config={{ apiBaseUrl: import.meta.env.VITE_API_URL }}>
<App />
</RhAuthProvider>
Vue, in main.ts:
const rhAuth = createRhAuth({ apiBaseUrl: import.meta.env.VITE_API_URL });
app.use(rhAuth);
router.beforeEach(rhAuth.authGuard);
In all three the session is restored once at start-up, before the first guard runs, so a page
reload keeps a signed-in user where they were. Until then initializing is true: show a spinner
while it is.
Sessions
The token is a JWT that lasts fifteen minutes. The kit renews it at 80% of its life, so a tab left
open stays signed in. If it expires anyway (a sleeping laptop, a tab in the background for an hour),
the next call gets a 401 and the session is cleared: the user sees that they are signed out.
Bearer token or cookie
The kit supports two modes. For a Ulabase service, use bearer.
Bearer (the default) keeps the token in localStorage and sends
Authorization: Bearer <token>. It works cross-origin.
Stay on 'bearer' unless the app is served from the service’s own origin.
Next.js and Nuxt can use a cookie: a first-party one, set by your own server and holding the
same bearer token. It needs no cookie support from the service. The /next and /nuxt subpaths do
this for you.
What it covers
Accounts: register, verify, login, logout, checkSession, getUserInfo,
renewToken, forgotPassword, resetPassword, changePassword, updateProfile. The flows
of Sign-up and login, with the redirects and token deliveries already handled.
Teams: getTeams, switchTeam, createTeam, listTeamMembers, updateMemberRole,
removeMember, and the invitation half: invite, getInvitation, activate, acceptInvite,
resendInvite. Switching team issues a new token, since the team is a claim in the token.
Consents: acceptConsents, for accepting terms and privacy policy. Use it with
Guards, which make acceptance mandatory on the server, where the client cannot
skip it.
Payments: getPlans, createCheckoutSession, openBillingPortal, getSubscription,
waitForSubscription, and seat licences with getLicenses / grantLicense / revokeLicense.
For a shop with one-off orders: getCatalog, createOrder, getOrder, waitForOrder,
readOrderRef. You need no Stripe.js and no publishable key: every Stripe page is hosted, and the
kit gives you its URL to navigate to. See Stripe billing for the service side.
The customer’s browser usually comes back to your site before Stripe’s webhook does.
waitForOrder waits for the webhook, and when it is late it rejects with a WaitTimeoutError, so
you can show "still confirming" for a payment that has not failed.
Cart: lines, quantities, subtotal and persistence, ready for any shop. The logic is pure and
takes the lines it works on, and each framework wraps it: useCart() in React and Vue,
RhCartService in Angular.
const cart = useCart();
cart.add({ productId: 'tee-classic/yellow-l', name: 'Classic T-shirt',
unitAmount: 2500, options: { colour: 'yellow', size: 'L' } });
await createOrder(config, cart.orderItems);
orderItems is the cart in the shape createOrder takes. Names, prices and pictures stay out:
the service reads them from its own catalog. The chosen options go with the order, as the line’s
metadata. A reference like tee-classic/yellow-l says which row of the catalog was bought; the
options give the seller "yellow, L" in separate fields.
The cart works without a session: it needs no AuthConfig and no auth provider, so visitors can
fill a basket before they sign in. It becomes an order at createOrder, which needs a config. It
lives in localStorage, so a reload keeps it; pass a storageKey when two of your apps share an
origin.
Errors are thrown as an ApiError, { status, message }, with the server’s own message in it.
A note on the users collection
register sends whatever you give it. If the service’s users collection has a JSON Schema, extra
fields are validated against it; if it has none, the server silently drops them and still
answers 201. If a profile field never appears, this is the usual cause.
Related pages
-
The
ulabaseCLI: configure the service the kit talks to from a file in git -
Tokens: what each token reaches
-
Stripe billing: the
stripefeature, its keys, and the permissions a guest checkout needs -
Guards: server-side rules the client cannot skip
-
Full-stack example: a complete app built on the kit
-
ulabase/kit: the source; each package’s README has the full API