@orbitneststudio/node
v0.12.0
Published
OrbitNest Node.js SDK for database operations, edge functions, and authentication
Downloads
429
Maintainers
Readme
@orbitneststudio/node
Official Node.js SDK for the OrbitNest platform — database, auth, edge functions, storage, realtime, background jobs, analytics, and migrations.
Server-side SDK. The API key is a credential — keep it on the server, never ship it to a browser. For client apps, use the anon key (RLS-enforced).
Install
npm install @orbitneststudio/nodeQuick start
import { createClient } from '@orbitneststudio/node';
// Only the API key is required — the project slug is decoded from the key's
// JWT, and baseUrl defaults to https://api.orbitnest.io.
const client = createClient({ apiKey: process.env.ORBITNEST_API_KEY! });
// Local dev / self-host:
// createClient({ apiKey, baseUrl: 'http://localhost:3002' })Database
Fluent query builder (recommended)
A Supabase-style, chainable builder. Every value is bound ($1, $2, …) — never
string-interpolated — and RLS still applies.
const { data, error } = await client.db
.from('posts')
.eq('status', 'published')
.gt('views', 100)
.order('created_at', { ascending: false })
.limit(10)
.select('id, title, views'); // → { rows, total }
// One row
const { data: post } = await client.db.from('posts').eq('id', id).single();
// First row or null (no error when empty)
const { data: maybe } = await client.db.from('posts').eq('slug', s).maybeSingle();Filter operators: eq, neq, gt, gte, lt, lte, like, ilike, in, is.
Pagination: limit(n), range(from, to) (inclusive), page(n, size).
Ordering: order(col, { ascending }).
Writes:
await client.db.from('posts').insert({ title: 'Hello' });
await client.db.from('posts').update(id, { title: 'Edited' });
await client.db.from('posts').delete(id);Raw SQL (full power)
const { data } = await client.db.query(
'SELECT * FROM users WHERE email = $1', [email], // always parameterize
);
// data: { rows, rowCount, fields }Type generation
Generate TypeScript interfaces from your live schema (OrbitNest's
supabase gen types equivalent):
import { writeFileSync } from 'fs';
import { generateTypes } from '@orbitneststudio/node';
writeFileSync('orbitnest.types.ts', await generateTypes(client.db));// then:
import type { Database, Posts } from './orbitnest.types';
const { data } = await client.db.from('posts').eq('id', id).single<Posts>();Excludes auth_* / internal tables by default; pass { tables } or { exclude }
to customize.
Migrations
Versioned, checksummed SQL migrations with a registry table, sequential execution, and rollback.
migrations/
001_init.sql
002_add_posts.sqlEach file may declare a rollback with a -- migrate:down marker:
-- 002: add posts
CREATE TABLE posts (id uuid PRIMARY KEY DEFAULT gen_random_uuid(), title text);
-- migrate:down
DROP TABLE posts;// Migrations run DDL — use a service-role key.
const admin = createClient({ apiKey: process.env.ORBITNEST_SERVICE_KEY!, migrationsDir: 'migrations' });
await admin.migrations.runAll(); // apply all pending (sequential, stop-on-failure)
await admin.migrations.runOne('002'); // apply one
await admin.migrations.rollback(); // roll back the most recent (runs its -- migrate:down)
await admin.migrations.rollback('002'); // roll back a specific one
await admin.migrations.getStatus(); // [{ migrationId, status, applied, checksumMismatch }]
admin.migrations.createMigration('add tags'); // scaffold 003_add_tags.sql
await admin.migrations.seed('seed.sql'); // run a (re-runnable) seed script, not recordedSafety: migrations never run in parallel, completed ones are never re-run, and a
migration whose file changed after being applied (checksum mismatch) aborts the
run. Each file/section runs inside one transaction — keep scripts
transaction-safe (no CREATE INDEX CONCURRENTLY).
Auth
await client.auth.signUp({ email, password });
await client.auth.signIn({ email, password });Edge functions
await client.functions.invoke('my-function', { body: { data: 'test' } });Storage
await client.storage.from('avatars').upload(path, file);Realtime
const ch = client.channel('orders')
.on('postgres_changes', { event: 'INSERT', table: 'orders' }, (e) => console.log(e.new))
.subscribe();On Node < 22 pass a WebSocket constructor: createClient({ ..., realtimeOptions: { WebSocket } }).
Errors
Every call returns { data, error } (never throws) — check error before using
data:
const { data, error } = await client.db.from('posts').select();
if (error) { /* handle */ }License
MIT
