= The Cloud Kit
:nav-title: Cloud Kit
:description: 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.
:keywords: frontend SDK, React authentication, Angular auth, Vue auth, Next.js backend, sign-up and login library, useAuth, usePayments
:group: Tools
:order: 20
RESTHeart 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.
[source,bash]
----
npm install @restheart-cloud/kit
----
[source,typescript]
----
import { checkSession, login, logout } from '@restheart-cloud/kit';
const config = { apiBaseUrl: 'https://f3a9c1.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
[cols="1,3"]
|===
| Package | What it adds
| `@restheart-cloud/kit`
| The core. Plain TypeScript, zero dependencies, works with any framework or none. Everything below is built on it.
| `@restheart-cloud/kit-ng`
| Angular — `provideRhAuth()`, `RhAuthService` and `RhPaymentsService` on signals, route guards, and an HTTP interceptor that attaches the token and clears the session on `401`.
| `@restheart-cloud/kit-react`
| React — `RhAuthProvider`, `useAuth()`, `usePayments()`, route guards. A `/next` subpath adds Next.js: middleware refresh, a first-party session cookie, the fragment→cookie bridge, and server actions.
| `@restheart-cloud/kit-vue`
| Vue — `createRhAuth()`, `useAuth()`, `usePayments()`, navigation guards. A `/nuxt` subpath does for Nuxt what `/next` does for Next.
|===
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`:
[source,typescript]
----
providers: [provideRhAuth({ apiBaseUrl: environment.apiUrl })]
----
React, near the root:
[source,tsx]
----
----
Vue, in `main.ts`:
[source,typescript]
----
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 `localStorage` and sends
`Authorization: Bearer `. It works cross-origin.
**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 `*.restheart.com` and your app does not, so that cookie is
*third-party* on every request the page makes — blocked by default in Safari and Firefox, and the
user's choice in Chrome. **No CORS configuration changes this**: the browser drops the cookie
before CORS is consulted.
Stay on `'bearer'` unless the app is served from the service's own origin.
====
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 link:{restheart-docs}/accounts/overview[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
xref:guards.adoc[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 xref:stripe.adoc[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.
**Cart** — lines, quantities, subtotal and persistence, which is the same code every shop was
about to write. The logic is pure and takes the lines it works on, so each framework wraps it
rather than reimplementing it: `useCart()` in React and Vue, `RhCartService` in Angular.
[source,tsx]
----
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 behind —
the service reads those from its own catalog — but the chosen **options travel, as the line's
`metadata`**. They are the one thing the service cannot work out for itself: a reference like
`tee-classic/yellow-l` says which row of the catalog was bought, and the seller reading the order
wants "yellow, L" in fields rather than decoded out of an id.
The cart is deliberately independent of the session: it needs no `AuthConfig` and no auth provider,
because a shop that asks people to sign in before it will hold a basket loses most of them at that
door. It becomes an order — which does need a config — at `createOrder`. It lives in
`localStorage`, so a reload does not empty it; pass a `storageKey` when two of your apps share an
origin.
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.
== Related pages
* xref:cli.adoc[The `rhc` CLI] — the other half of a starter: the service the kit talks to, configured from a file in git
* xref:tokens.adoc[Tokens] — what a service token reaches, and what it does not
* xref:stripe.adoc[Stripe Billing] — the `stripe` plugin, its keys, and the permissions a guest checkout needs
* xref:guards.adoc[Guards] — server-side rules the client cannot skip
* xref:full-stack-example.adoc[Full-Stack Example] — a complete app built on the kit
* https://github.com/SoftInstigate/restheart-cloud-kit[restheart-cloud-kit] — the monorepo; each package's README carries the full API