RESTHeart Cloud β The Cloud Kit
CloudRESTHeart Cloud gives you a backend without writing one. The Cloud Kit is the other half: the client code that talks to it β sign-up, sign-in, email verification, password reset, teams and invitations, and, for an app that sells something, subscriptions and orders.
npm install @restheart-cloud/kit
import { checkSession, login, logout } from '@restheart-cloud/kit';
const config = { apiBaseUrl: 'https://ea820b.eu-central-1-free-1.restheart.com' };
const user = await checkSession(config); // UserInfo, or null
await login(config, 'user@example.com', 'secret');
await logout(config);
That is the whole shape of it. Every function takes the same config as its first argument, so
nothing is initialised, registered, or held in a module global.
Why not just fetch
Because none of these flows is one request.
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 is the accumulated shape of those flows, against restheart-accounts and
restheart-stripe as RESTHeart Cloud runs them. It has no dependencies of its own and no opinion
about your 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 β |
An adapter is a wrapper, not a fork: the same functions, exposed the way that framework expects
state to arrive. Payments are a separate surface from auth in each of them, because a subscription
is not a session β it loads on sign-in, reloads on team switch, and stays untouched when the
service has no stripe plugin.
Angular and Vue want the core installed alongside; React and Vue pull it in as a regular 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 does not bounce a signed-in user to the login page. Until that settles, initializing is
true β render a spinner on it, not a redirect.
Sessions
The token is a JWT with a fifteen-minute life. The kit schedules a renewal at 80% of its TTL, so a
tab left open stays signed in without the app or the user noticing. If it does expire β a sleeping
laptop, a tab backgrounded for an hour β the next call gets a 401 and the session is cleared: the
user sees "signed out" rather than a silent failure.
Bearer, not cookie
|
Warning
|
The kit supports two modes, and for a RESTHeart Cloud service only one of them works. Bearer (the default) keeps the token in Cookie mode has the server manage an HttpOnly JWT cookie, and needs the app and the service to
share an origin. Your service lives on Stay on |
Next.js and Nuxt are the exception that proves the rule: their cookie is a first-party one, set
by your own server and holding the same bearer token. It needs no cookie support from RESTHeart at
all. That is what the /next and /nuxt subpaths are for.
What it covers
Accounts β register, verify, login, logout, checkSession, getUserInfo,
renewToken, forgotPassword, resetPassword, changePassword, updateProfile. The flows
documented under Sign-up, OAuth & Invitations, 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 teams mints a new token, since the team is a claim in the old one.
Consents β acceptConsents, for the terms-and-privacy pattern. Pairs with
Guards, which is what makes acceptance mandatory server-side rather than a dialog the
client can be talked out of showing.
Payments β getPlans, createCheckoutSession, openBillingPortal, getSubscription,
waitForSubscription, and seat licences with getLicenses / grantLicense / revokeLicense.
For a shop rather than a subscription: getCatalog, createOrder, getOrder, waitForOrder,
readOrderRef. No Stripe.js and no publishable key β every Stripe page is hosted, so the kit hands
you a URL to navigate to. See Stripe Billing for the service side.
waitForOrder exists because the customer’s browser usually beats Stripe’s webhook back to your
site. It rejects with a distinct WaitTimeoutError, so a late webhook renders as "still
confirming" rather than as a failed payment.
Errors arrive as { status, message } β an ApiError, thrown, with the server’s own sentence 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. A profile field that never appears is usually this, and not the kit.
See also
-
The
rhcCLI β the other half of a starter: the service the kit talks to, configured from a file in git -
Tokens β what a service token reaches, and what it does not
-
Stripe Billing β the
stripeplugin, 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
-
restheart-cloud-kit β the monorepo; each package’s README carries the full API