@orbiocloud/client
v0.2.0
Published
TypeScript SDK for the OrbioCloud platform API
Readme
@orbiocloud/client
TypeScript SDK for the OrbioCloud platform API. Auth, collections, storage, billing, webhooks and realtime tokens behind one typed client.
Works in Node 18+ and modern browsers. No runtime dependencies — it uses the global
fetch.
Install
npm install @orbiocloud/clientQuick start
import { OrbioClient } from '@orbiocloud/client';
const orbio = new OrbioClient({ apiKey: process.env.ORBIO_API_KEY! });
await orbio.auth.signIn({ email: '[email protected]', password: '...' });
const { items: posts } = await orbio.collections.documents('posts').list({ limit: 20 });apiKey is required. baseUrl defaults to https://api.orbiocloud.com and only needs
setting if you are pointed at another environment.
Keep your API key server-side. It authenticates as your tenant. In a browser app, call your own backend and let it hold the key.
Auth
await orbio.auth.signUp({ email, password, metadata: { plan: 'free' } });
await orbio.auth.signIn({ email, password });
await orbio.auth.signOut();
orbio.auth.getSession(); // current session or null
orbio.auth.getAccessToken(); // raw token or null
await orbio.auth.getUser();
await orbio.auth.refreshSession();
await orbio.auth.resetPassword(email); // note: a plain string, not an object
const unsubscribe = orbio.auth.onAuthStateChange((event, session) => {
if (event === 'SIGNED_OUT') redirectToLogin();
});After signIn, the access token is attached to subsequent requests automatically.
Signup rate limits
signUp accepts an optional turnstile_token. Supplying a valid one raises your budget
from 1 signup/hour/IP to 3/hour/IP. An invalid token is rejected with 403, so send a
real one or omit the field.
await orbio.auth.signUp({ email, password, turnstile_token: tokenFromWidget });Collections
// Collections themselves
const { items, total, has_more } = await orbio.collections.list({ page: 1, limit: 20 });
await orbio.collections.create({ slug: 'posts', name: 'Posts' });
await orbio.collections.get('posts');
await orbio.collections.update('posts', { name: 'Blog posts' });
await orbio.collections.delete('posts');
// Documents inside a collection
const posts = orbio.collections.documents<{ title: string }>('posts');
await posts.list({ page: 1, limit: 20, filter: { published: true } });
await posts.create({ title: 'Hello' });
await posts.get(id);
await posts.update(id, { title: 'Updated' });
await posts.delete(id);List endpoints return { items, page, limit, total, has_more }.
Storage
await orbio.storage.upload(file, { filename: 'avatar.png' }); // File or Blob
await orbio.storage.list();
await orbio.storage.get(fileId);
await orbio.storage.delete(fileId);Billing
await orbio.billing.createCheckout({
price_id: 'price_123',
success_url: 'https://example.com/done',
cancel_url: 'https://example.com/cancel',
});
await orbio.billing.getSubscription();
await orbio.billing.createPortalSession();Webhooks
await orbio.webhooks.createEndpoint({ url, events: ['user.created'] });
await orbio.webhooks.listEndpoints();
await orbio.webhooks.getEndpoint(id);
await orbio.webhooks.updateEndpoint(id, { events: [...] });
await orbio.webhooks.deleteEndpoint(id);
await orbio.webhooks.listDeliveries({ endpoint_id: id });Realtime
const { token, expires_in } = await orbio.getRealtimeToken();Returns a short-lived token for a realtime connection. Mint a fresh one per session rather than caching it.
Error handling
Every failure throws an OrbioError carrying the real HTTP status.
import { OrbioError } from '@orbiocloud/client';
try {
await orbio.auth.signIn({ email, password });
} catch (err) {
if (err instanceof OrbioError) {
if (err.isRateLimited) {
// Back off. err.retryAfter is seconds, when the server said.
await wait((err.retryAfter ?? 60) * 1000);
} else if (err.status === 401) {
showInvalidCredentials();
}
}
}| Property | Meaning |
| --- | --- |
| status | HTTP status as returned by the server |
| message | Server-supplied error message |
| code | Machine-readable code, when the server sends one |
| retryAfter | Seconds to wait, on 429 — from Retry-After or retry_after |
| isRateLimited | true when status === 429 |
Do not retry a 429 with different credentials
Logins are throttled per account as well as per IP, so you can be rate limited while
using entirely valid credentials. Treating 429 as an auth failure and retrying makes it
worse. Branch on isRateLimited before you branch on anything else.
Versions before
0.2.0reported a hardcoded400/401on every error regardless of what the server returned, so a429was indistinguishable from bad credentials. If you are pinned below0.2.0, upgrade before relying onstatus.
Security model
Sessions are held in memory only. This client never writes to localStorage,
sessionStorage or cookies. That is deliberate: a token in localStorage is readable by
any script on the page, so one XSS becomes a stolen session. The trade-off is that a
session does not survive a page reload — if you need persistence in a browser app, store
the session yourself somewhere you have decided is appropriate (an httpOnly cookie set by
your own backend is the usual answer), rather than expecting the SDK to do it for you.
Your API key authenticates as your whole tenant. Keep it on a server. If a browser needs data, call your own backend and let that hold the key. Shipping the key to a client bundle exposes every tenant operation this SDK can perform.
Zero runtime dependencies. The package has no dependencies, only the global fetch,
so installing it adds no transitive supply-chain surface.
Node 18+ is required for global fetch.
License
MIT
