Developers/SDK/JavaScript SDK
JavaScript SDK
The Supero JavaScript SDK (@supero/js-lib) is a Node.js client for authenticating, running CRUD, and calling platform services against api.supero.dev.
Overview
The JavaScript SDK gives Node.js backends the same high-level surface as the Python SDK: connect once, then use string-keyed CRUD (org.create(...)), or a schema-aware Proxy (org.Customer.create(...)) that resolves any PascalCase property on your connected instance to the matching snake_case schema type. It is a server-side (Node.js) client — it talks to the API over the Node http/https modules, so it is meant for backends, scripts, and serverless functions rather than direct browser bundles.
bash
npm install @supero/js-libℹ️ Requirements
Node.js 14 or later. The package is versioned independently from the platform (current SDK version 2.0.0, available as supero.VERSION at runtime).
javascript
const supero = require('supero');
// or, for a specific manager only:
// const { CrudManager, AuthenticationError } = require('supero');Connecting
There are three ways to obtain a connected instance. Each returns a Proxy-wrapped Supero object (referred to as org below) — the Proxy resolves any capitalized property you have not defined yourself into a schema accessor, so org.Order, org.Customer, org.AnythingInYourSchema all work without any generated code.
Email + password login
javascript
const org = await supero.login({
domainName: 'acme-corp',
email: '[email protected]',
password: 'secret',
host: 'api.supero.dev', // default
port: 443, // default
});Existing API key (no login round trip)
javascript
const org = supero.connect('acme-corp', {
apiKey: process.env.SUPERO_API_KEY, // 'ak_...'
});Existing JWT or API key (quickstart)
javascript
const org = supero.quickstart({
domainName: 'acme-corp',
jwtToken: savedAccessToken, // or apiKey: 'ak_...'
});login() authenticates with POST /api/v1/auth/login and stores the returned access token; connect() and quickstart() skip that round trip and authenticate every request with the X-API-Key header (or the JWT you pass in) instead. Never send an API key as a Bearer token yourself — the SDK sets the correct header for you.
| Option | Default | Notes |
|---|---|---|
| domainName | (required) | Your domain name |
| host | 'api.supero.dev' | Override for on-premise deployments |
| port | 443 | |
| apiKey | null | Opaque secret starting 'ak_...' |
| jwtToken | null | Bearer access token |
| projectName | 'default-project' | Used to stamp fq_name on create() |
💡 Environment variables
supero.fromEnv() builds a connected instance from SUPERO_DOMAIN (or SUPERO_TENANT), SUPERO_API_KEY (or SUPERO_JWT), SUPERO_HOST, SUPERO_PORT and SUPERO_PROJECT — handy for scripts and CI.
To create a brand-new tenant domain rather than connecting to an existing one, use supero.registerDomain({domainName, adminEmail, adminPassword, ...}), which also returns a connected, Proxy-wrapped instance.
String-keyed CRUD
org exposes create/get/list/update/delete directly, taking the schema type as a lowercase string. This is the most direct way to call the CRUD API and works for any schema type without needing a matching PascalCase accessor.
javascript
// Create — 'name' is required; fq_name/parent_type are stamped for you
const customer = await org.create('customer', {
name: 'jane-smith',
email: '[email protected]',
plan: 'growth',
});
console.log(customer.uuid, customer.fq_name);
// Read
const same = await org.get('customer', customer.uuid);
// List (returns a plain array, already unwrapped from the envelope)
const customers = await org.list('customer', { limit: 25, offset: 0 });
// Update
const updated = await org.update('customer', customer.uuid, { plan: 'enterprise' });
// Delete
const ok = await org.delete('customer', customer.uuid); // true/false
// Chainable query with filters
const bigOrders = await org.query('order').where('total', 'gt', 100).all();Every created object gets the standard system fields: uuid, fq_name, parent_type, parent_uuid, created_at, updated_at. Records are always addressed by uuid.
ℹ️ org.crud
org.create/get/list/update/delete/query are thin convenience wrappers over the full CrudManager at org.crud, which also exposes find(), count(), exists(), getByName(), getOrCreate(), updateOrCreate(), bulkCreate/bulkUpdate/bulkDelete/bulkGet, and reference helpers (setRef/getRef/updateRef/removeRef).
Schema-aware access & the query builder
Instead of passing the type as a string, you can address any schema type as a capitalized property on org. org.Order resolves to the same operations as org.crud on the "order" type — the Proxy converts PascalCase to the snake_case schema name for you.
javascript
const order = await org.Order.create({ name: 'order-001', total: 99, status: 'pending' });
const mine = await org.Order.get(order.uuid);
const pending = await org.Order.list({ status: 'pending' });
await org.Order.update(order.uuid, { status: 'shipped' });
await org.Order.delete(order.uuid);
// Chainable query builder
const big = await org.Order
.query()
.where('total', 'gt', 100)
.orderBy('-created_at')
.limit(10)
.all();
const first = await org.Order.query().where('status', 'pending').first();
const n = await org.Order.query().where('status', 'pending').count();The query builder also accepts Django-style lookup suffixes directly as filter keys, and exposes matching chain methods:
| Lookup | Example |
|---|---|
| exact / eq | .where('status', 'pending') or .eq('status', 'pending') |
| gt / gte / lt / lte | .gte('total', 100) |
| in / nin | .in('status', ['pending', 'paid']) |
| contains / icontains | .icontains('email', '@acme.com') |
| startswith / endswith | .startswith('name', 'ORD-') |
| isnull | .isnull('cancelled_at', true) |
Terminal methods: .all() (array), .first() (single object or null), .count(), .exists(), .get(uuid), .delete(uuid), .deleteAll() (deletes everything matching the current filters — use with care).
Namespaces
Namespace is an orthogonal label used to isolate collections of the same schema type (for example the same "customer" schema reused by two integrations). Scope any CRUD call to a namespace with org.crud.ns(...):
javascript
await org.crud.ns('billing').customer.create({ name: 'acme' });
const billingCustomers = await org.crud.ns('billing').customer.list();Files, AI, and platform services
Beyond CRUD, a connected org exposes managers for the other platform capabilities.
Files — org.files
javascript
const ref = await org.files.upload('/tmp/logo.png');
console.log(ref.url, ref.mimeType, ref.sizeBytes);
// Upload and attach to a record's file/image field in one call
await org.files.attach(customer.uuid, 'customer', 'avatar', '/tmp/avatar.png');
await org.files.delete(ref.fileId);AI — org.ai
org.ai talks to the AI-generated assistant configured for your domain — it can read your schemas as tools and answer questions or take actions grounded in your data.
javascript
const reply = await org.ai.chat('Summarize today\'s pending orders');
console.log(reply.content);
// Shorthand: just the text back
const answer = await org.ai.ask('How many customers are on the growth plan?');
// Streaming
for await (const chunk of org.ai.chatStream('Draft a follow-up email')) {
process.stdout.write(chunk);
}Platform services — org.services
The managed services catalog is the primary way to reach third-party integrations (email, SMS, WhatsApp, push, Slack, Stripe, PayPal, Razorpay, S3, calendar, Drive, OTP, and social login), imported and configured per project from the catalog and then invoked with a single execution call. org.services gives typed convenience wrappers over that same call:
javascript
await org.services.email.send({
to: '[email protected]',
subject: 'Welcome!',
bodyHtml: '<p>Thanks for signing up.</p>',
});
await org.services.slack.message({ channel: '#orders', text: 'New order placed' });
const session = await org.services.stripe.checkout({
amount: 4900,
productName: 'Pro plan',
successUrl: 'https://example.com/thanks',
});
// Generic escape hatch for any catalog service
await org.services.exec('github', 'create_issue', { repo: 'acme/app', title: 'Bug' });Transactional operations
For commerce and workflow-style domains, org.transactional (alias org.txn) provides higher-level actions — cart, order, payment, booking and similar operations — implemented as event-bound actions on top of the same services execution path, rather than raw CRUD writes.
javascript
const cart = await org.transactional.cart.getOrCreateOpenCart(customer.uuid);
await org.transactional.cart.add({
productUuid: product.uuid,
quantity: 2,
unitPrice: 49.0,
productName: 'Widget Pro',
});
const order = await org.transactional.cart.checkout(cart.uuid);
await org.transactional.order.fulfill(order.uuid, 'Shipped via courier');⚠️ Cumulative actions
Actions like cart.add and payment.refund are cumulative (each call adds to a running quantity or refunded amount) and are not automatically retried by the engine. Read the current record before retrying a call that may have already succeeded, or design your own idempotency around it.
Error handling
HTTP failures are raised as typed errors so you can branch on the failure mode instead of parsing status codes yourself.
| Error | Meaning |
|---|---|
| AuthenticationError | Login or authentication failed (401) |
| AuthorizationError | Authenticated but not permitted (403) |
| NotFoundError | No object matches the request (404) |
| ValidationError | Request failed validation (400/422) |
| RateLimitError | Rate limit exceeded (429) |
| ConnectionError | Network-level failure — could not reach the server |
| HttpError | Base class for the above; carries statusCode and the parsed response body |
javascript
const { AuthenticationError, NotFoundError, RateLimitError } = require('supero');
try {
const customer = await org.get('customer', someUuid);
} catch (err) {
if (err instanceof NotFoundError) {
console.log('Customer not found');
} else if (err instanceof AuthenticationError) {
console.log('Check your credentials or API key');
} else if (err instanceof RateLimitError) {
console.log('Rate limited, back off and retry');
} else {
throw err;
}
}get() on org.crud (and the schema-Proxy get()) resolves to null instead of throwing when the object is not found, so use exists() or a null check where that is more convenient than a try/catch.
Next steps
- •Python SDK — the equivalent server-side client for Python backends
- •API Overview & Conventions — the underlying CRUD, query, and auth endpoints this SDK calls
- •Managed Integrations & Provider Catalog — the full managed services catalog and how to import and configure a service
- •Schema Reference — fields, references, and namespaces for the schemas you CRUD against
On this page