BaasixBaasix

JavaScript SDK

← Back to Documentation Home

The official JavaScript/TypeScript SDK for Baasix provides a type-safe, easy-to-use client for integrating with your Baasix backend.

Features

  • 🌐 Universal — Works in browsers, Node.js, and React Native
  • 🔐 Flexible Auth — JWT tokens, HTTP-only cookies, OAuth (Google, Facebook, Apple, GitHub)
  • 💾 Customizable Storage — LocalStorage, AsyncStorage, or custom adapters
  • 📝 Type-Safe — Full TypeScript support with generics
  • 🔄 Auto Token Refresh — Seamless token management
  • 🏢 Multi-Tenant — Built-in tenant switching and invitation support
  • Query Builder — Fluent API for complex queries with 50+ filter operators
  • 📡 Realtime — WebSocket subscriptions for live data updates
  • 📁 File Management — Upload, download, and transform assets
  • 🔀 Workflows — Execute and monitor workflow executions
  • 🔔 Notifications — User notification system with realtime delivery
  • 🗃️ Migrations — Database schema migration management
  • 📊 Reports — Analytics and aggregation queries
  • 🔃 Sort/Reorder — Drag-and-drop style item reordering

Installation

npm install @tspvivek/baasix-sdk
# or
yarn add @tspvivek/baasix-sdk
# or
pnpm add @tspvivek/baasix-sdk

TypeScript Types Generation

For full type safety, use the Baasix CLI to generate TypeScript types from your schemas:

# Generate types from running Baasix instance
npx baasix generate --url http://localhost:8056 --output ./src/types/baasix.d.ts

Then use the generated types with the SDK:

import { createBaasix } from '@tspvivek/baasix-sdk';
import type { BaasixCollections, Product } from './types/baasix';

// Create typed client
const baasix = createBaasix<BaasixCollections>({
  url: 'https://api.example.com',
});

// All queries are now fully typed
const { data } = await baasix.items('products').find({
  filter: { price: { gte: 10 } },
});
// data is typed as Product[]

See the CLI Guide for more details on type generation.

Quick Start

import { createBaasix } from '@tspvivek/baasix-sdk';

// Create client
const baasix = createBaasix({
  url: 'https://your-baasix-instance.com',
});

// Login
const { user, token } = await baasix.auth.login({
  email: 'user@example.com',
  password: 'password123',
});

// Query items
const { data: products } = await baasix.items('products').find({
  filter: { status: { eq: 'active' } },
  sort: { createdAt: 'desc' },
  limit: 10,
});

// Create item
const productId = await baasix.items('products').create({
  name: 'New Product',
  price: 29.99,
});

Configuration

Basic Configuration

import { createBaasix } from '@tspvivek/baasix-sdk';

const baasix = createBaasix({
  url: 'https://api.example.com', // Required: Your Baasix URL
  authMode: 'jwt', // 'jwt' (default) or 'cookie'
  timeout: 30000, // Request timeout in ms
  autoRefresh: true, // Auto-refresh tokens
  onAuthStateChange: (event, user) => {
    // Auth state callback
    console.log('Auth changed:', event, user);
  },
});

React Native Setup

import { createBaasix, AsyncStorageAdapter } from '@tspvivek/baasix-sdk';
import AsyncStorage from '@react-native-async-storage/async-storage';

const baasix = createBaasix({
  url: 'https://api.example.com',
  storage: new AsyncStorageAdapter(AsyncStorage),
});
const baasix = createBaasix({
  url: 'https://api.example.com',
  authMode: 'cookie',
  credentials: 'include',
});

Server-Side / Service Account

const baasix = createBaasix({
  url: 'https://api.example.com',
  token: 'your-static-api-token',
});

Authentication

Login & Register

// Register
const { user, token } = await baasix.auth.register({
  email: 'newuser@example.com',
  password: 'securepassword',
  firstName: 'John',
  lastName: 'Doe',
});

// Login
const { user, token } = await baasix.auth.login({
  email: 'user@example.com',
  password: 'password123',
});

// Logout
await baasix.auth.logout();

OAuth / Social Login

// Redirect to OAuth provider
const url = baasix.auth.getOAuthUrl({
  provider: 'google', // 'google' | 'facebook' | 'apple' | 'github'
  redirectUrl: 'https://myapp.com/auth/callback',
});
window.location.href = url;

// In your callback page
const params = new URLSearchParams(window.location.search);
const token = params.get('token');

if (token) {
  const { user } = await baasix.auth.handleOAuthCallback(token);
  console.log('Logged in as:', user.email);
}
// Send magic link
await baasix.auth.sendMagicLink({
  email: 'user@example.com',
  redirectUrl: 'https://myapp.com/auth/verify',
});

// Verify (in callback page)
const { user, token } = await baasix.auth.verifyMagicLink(tokenFromUrl);

Multi-tenant

// Get available tenants
const tenants = await baasix.auth.getTenants();

// Switch tenant
const { user, token } = await baasix.auth.switchTenant('tenant-uuid');

// Send invitation
await baasix.auth.sendInvite({
  email: 'newuser@example.com',
  roleId: 'editor-role-uuid',
  tenantId: 'tenant-uuid',
  redirectUrl: 'https://myapp.com/accept-invite',
});

Items (CRUD Operations)

Basic Operations

const products = baasix.items('products');

// Find all
const { data, totalCount } = await products.find({
  filter: { status: { eq: 'active' } },
  sort: { createdAt: 'desc' },
  limit: 20,
  page: 1,
});

// Find one
const product = await products.findOne('product-uuid');

// Create
const id = await products.create({
  name: 'New Product',
  price: 29.99,
});

// Update
await products.update('product-uuid', { price: 24.99 });

// Delete
await products.delete('product-uuid');

Query Builder

const results = await baasix
  .items('posts')
  .query()
  .select('*', 'author.*', 'comments.*')
  .filter({ status: { eq: 'published' } })
  .sort({ createdAt: 'desc' })
  .limit(10)
  .page(1)
  .get();

Filter Operators

// Comparison
{ price: { eq: 100 } }        // equals
{ price: { neq: 100 } }       // not equals
{ price: { gt: 100 } }        // greater than
{ price: { gte: 100 } }       // greater than or equal
{ price: { lt: 100 } }        // less than
{ price: { lte: 100 } }       // less than or equal

// String
{ name: { like: '%Product%' } }
{ name: { ilike: '%product%' } }  // case-insensitive
{ name: { startsWith: 'Pro' } }
{ name: { contains: 'duct' } }

// List
{ status: { in: ['active', 'pending'] } }
{ price: { between: [10, 100] } }

// Null
{ deletedAt: { isNull: true } }

// Array (PostgreSQL)
{ tags: { arraycontains: ['featured'] } }

// Logical
{ AND: [{ status: { eq: 'active' } }, { price: { gt: 0 } }] }
{ OR: [{ featured: { eq: true } }, { views: { gt: 1000 } }] }

// Dynamic variables
{ author_Id: { eq: '$CURRENT_USER' } }
{ createdAt: { gte: '$NOW-DAYS_30' } }

Bulk Operations

// Create many
const ids = await products.createMany([
  { name: 'Product 1', price: 10 },
  { name: 'Product 2', price: 20 },
]);

// Update many - apply same data to multiple IDs
await products.updateMany(['uuid-1', 'uuid-2'], { status: 'archived' });

// Delete many
await products.deleteMany(['uuid-1', 'uuid-2']);

Import Data

// Import from CSV file
const fileInput = document.querySelector('input[type="file"]');
const result = await products.importCSV(fileInput.files[0]);
console.log(`Imported ${result.imported} items`);

// Import from JSON file
const result = await products.importJSON(jsonFile);

// Import from array (bulk create)
const ids = await products.createMany([
  { name: 'Product 1', price: 29.99 },
  { name: 'Product 2', price: 39.99 },
]);

Sort / Reorder Items

// Move item1 before item2
await products.sortItem('item1-uuid', 'item2-uuid');

// Move item1 after item2
await products.sortItem('item1-uuid', 'item2-uuid', 'after');

// Reorder multiple items (set explicit order)
await products.reorder(['item3-uuid', 'item1-uuid', 'item2-uuid']);

Realtime Subscriptions

The SDK supports real-time data updates via WebSocket connections, powered by PostgreSQL WAL (Write-Ahead Log) for reliable change capture.

Setup

npm install socket.io-client
import { io } from 'socket.io-client';

// Set the socket client
baasix.realtime.setSocketClient(io);

// Connect
await baasix.realtime.connect();

Subscribe to Collections

// Subscribe to all changes
const unsubscribe = baasix.realtime.subscribe('products', (payload) => {
  console.log(`${payload.action}:`, payload.data);
  // payload.action: 'create' | 'update' | 'delete'
});

// Subscribe to specific events
baasix.realtime.on('orders', 'create', (data) => {
  console.log('New order:', data);
});

// Unsubscribe
unsubscribe();

Supabase-style Channel API

const channel = baasix.realtime
  .channel('products')
  .on('INSERT', (payload) => console.log('New:', payload))
  .on('UPDATE', (payload) => console.log('Updated:', payload))
  .on('DELETE', (payload) => console.log('Deleted:', payload))
  .subscribe();

// Later
channel.unsubscribe();

Custom Rooms

Custom rooms allow real-time communication between users for use cases like chat, games, or collaborative features.

// Join a room
await baasix.realtime.joinRoom('game:lobby');

// Send a message to the room
await baasix.realtime.sendToRoom('game:lobby', 'chat', {
  text: 'Hello everyone!',
});

// Listen for room messages
const unsubscribe = baasix.realtime.onRoomMessage('game:lobby', 'chat', (data) => {
  console.log(`${data.sender.userId}: ${data.payload.text}`);
});

// Listen for users joining/leaving
baasix.realtime.onRoomUserJoined('game:lobby', (data) => {
  console.log(`User ${data.userId} joined`);
});

baasix.realtime.onRoomUserLeft('game:lobby', (data) => {
  console.log(`User ${data.userId} left`);
});

// Leave the room
await baasix.realtime.leaveRoom('game:lobby');

Custom Event Handlers

Invoke custom server-side handlers registered by extensions:

// Call a custom server handler
const result = await baasix.realtime.invoke('game:roll-dice', { sides: 6 });
console.log('Dice result:', result);

Connection Management

// Check status
if (baasix.realtime.isConnected) {
  console.log('Connected');
}

// Listen for changes
baasix.realtime.onConnectionChange((connected) => {
  console.log('Realtime:', connected ? 'online' : 'offline');
});

// Disconnect
baasix.realtime.disconnect();

Files

Upload

// Browser
const metadata = await baasix.files.upload(fileInput.files[0], {
  title: 'Product Image',
  folder: 'products',
  isPublic: true,
  onProgress: (progress) => console.log(`${progress}%`),
});

// React Native
const metadata = await baasix.files.upload({
  uri: result.uri,
  name: 'photo.jpg',
  type: 'image/jpeg',
});

Get Asset URLs

// Original
const url = baasix.files.getAssetUrl('file-uuid');

// With transformations
const thumbnailUrl = baasix.files.getAssetUrl('file-uuid', {
  width: 200,
  height: 200,
  fit: 'cover',
  format: 'webp',
  quality: 80,
});

Download

const blob = await baasix.files.download('file-uuid');

Schemas

// List schemas
const schemas = await baasix.schemas.find();

// Create schema with validation and default values
await baasix.schemas.create('products', {
  name: 'Product',
  timestamps: true,
  fields: {
    id: {
      type: 'UUID',
      primaryKey: true,
      defaultValue: { type: 'UUIDV4' },
    },
    sku: {
      type: 'SUID',
      unique: true,
      defaultValue: { type: 'SUID' },
    },
    name: {
      type: 'String',
      allowNull: false,
      values: { length: 255 },
      validate: {
        notEmpty: true,
        len: [3, 255],
      },
    },
    price: {
      type: 'Decimal',
      values: { precision: 10, scale: 2 },
      validate: {
        min: 0,
        max: 999999.99,
      },
    },
    quantity: {
      type: 'Integer',
      defaultValue: 0,
      validate: {
        isInt: true,
        min: 0,
      },
    },
    email: {
      type: 'String',
      validate: {
        isEmail: true,
      },
    },
    website: {
      type: 'String',
      validate: {
        isUrl: true,
      },
    },
    status: {
      type: 'String',
      defaultValue: 'draft',
    },
    isActive: {
      type: 'Boolean',
      defaultValue: true,
    },
    sortOrder: {
      type: 'Integer',
      defaultValue: { type: 'AUTOINCREMENT' },
    },
    publishedAt: {
      type: 'DateTime',
      defaultValue: { type: 'NOW' },
    },
  },
});

// Create relationship
await baasix.schemas.createRelationship('products', {
  type: 'M2O',
  target: 'categories',
  name: 'category',
  alias: 'products',
});

Validation Rules

RuleTypeDescription
minnumberMinimum value for numeric fields
maxnumberMaximum value for numeric fields
isIntbooleanValidate as integer
notEmptybooleanString must not be empty
isEmailbooleanValidate email format
isUrlbooleanValidate URL format
len[min, max]String length range
is / matchesregexPattern matching

Default Value Types

TypeDescription
UUIDV4Random UUID v4
SUIDShort unique ID
NOWCurrent timestamp
AUTOINCREMENTAuto-increment integer
SQLCustom SQL expression

Users & Roles (Admin)

Users

// List users
const { data: users } = await baasix.users.find({
  filter: { status: { eq: 'active' } },
});

// Create user
const userId = await baasix.users.create({
  email: 'user@example.com',
  password: 'password123',
  firstName: 'John',
  role_Id: 'role-uuid',
});

// Admin password change
await baasix.users.changePassword(userId, 'newPassword');

// Suspend/Activate
await baasix.users.suspend(userId);
await baasix.users.activate(userId);

Roles

// List roles
const { data: roles } = await baasix.roles.find();

// Create role
const roleId = await baasix.roles.create({
  name: 'Editor',
  description: 'Content editors',
  appAccess: true,
});

Workflows

// Execute workflow
const result = await baasix.workflows.execute('workflow-uuid', {
  orderId: 'order-123',
});

// Monitor execution (with realtime)
const unsubscribe = baasix.realtime.subscribeToExecution(result.executionId, (update) => {
  console.log('Progress:', update.progress, '%');
  if (update.status === 'complete') {
    console.log('Done!', update.result);
  }
});

Reports & Analytics

Generate Report (POST)

Use generate() to create a report with a POST request:

const report = await baasix.reports.generate('orders', {
  aggregate: {
    revenue: { function: 'sum', field: 'total' },
    orders: { function: 'count', field: 'id' },
  },
  groupBy: ['category'],
  filter: { status: { eq: 'completed' } },
  dateRange: {
    start: '2025-01-01',
    end: '2025-12-31',
  },
});

Query Report (GET)

Use query() to fetch a report with query parameters:

const report = await baasix.reports.query('orders', {
  aggregate: { total: { function: 'sum', field: 'amount' } },
  groupBy: ['status'],
});

Multi-Collection Stats

Get statistics for multiple collections in a single request:

const stats = await baasix.reports.getStats([
  {
    name: 'total_products',
    collection: 'products',
    query: {
      aggregate: { count: { function: 'count', field: '*' } },
    },
  },
  {
    name: 'total_orders',
    collection: 'orders',
    query: {
      aggregate: {
        count: { function: 'count', field: '*' },
        total_amount: { function: 'sum', field: 'amount' },
      },
    },
  },
]);

Aggregation, Count & Distinct

// Aggregation query on items
const results = await baasix.reports.aggregate('orders', {
  aggregate: {
    total: { function: 'sum', field: 'amount' },
    avg: { function: 'avg', field: 'amount' },
  },
  groupBy: ['status'],
});

// Quick count
const activeUsers = await baasix.reports.count('users', {
  status: { eq: 'active' },
});

// Get distinct values
const categories = await baasix.reports.distinct('products', 'category');

Notifications

// Get user notifications
const { data } = await baasix.notifications.find({
  limit: 20,
  filter: { seen: { eq: false } },
});

// Get unread count
const count = await baasix.notifications.getUnreadCount();

// Mark notifications as seen
await baasix.notifications.markAsSeen(['id1', 'id2']);
// Or mark all as seen
await baasix.notifications.markAsSeen();

// Delete notifications
await baasix.notifications.delete(['id1', 'id2']);

// Send notification (admin only)
await baasix.notifications.send({
  type: 'alert',
  title: 'System Update',
  message: 'Maintenance scheduled for tonight',
  userIds: ['user1-uuid', 'user2-uuid'],
});

// Cleanup old notifications (admin only)
await baasix.notifications.cleanup(30); // older than 30 days

Migrations (Admin)

// Check migration status
const status = await baasix.migrations.status();
console.log(`Pending: ${status.pendingCount}`);

// Get pending migrations
const pending = await baasix.migrations.pending();

// Run pending migrations
const result = await baasix.migrations.run();
console.log(`Completed: ${result.summary.completed}`);

// Run with options
const result = await baasix.migrations.run({
  step: 1, // Run only 1 migration
  dryRun: true, // Preview without executing
});

// Rollback a specific migration
await baasix.migrations.rollback('20231201000000');

// Rollback last batch
await baasix.migrations.rollbackBatch();

// Create new migration file
const { filepath } = await baasix.migrations.create('add_status_column', {
  type: 'schema',
  description: 'Add status column to orders',
});

// Mark migrations as completed (without running)
await baasix.migrations.markCompleted('20231201000000');
await baasix.migrations.markAllCompleted();

Error Handling

import { BaasixError } from '@tspvivek/baasix-sdk';

try {
  await baasix.items('products').create({ name: 'Test' });
} catch (error) {
  if (error instanceof BaasixError) {
    console.error('API Error:', error.message);
    console.error('Status:', error.status);
    console.error('Code:', error.code);
  }
}

TypeScript Support

// Define your types
interface Product {
  id: string;
  name: string;
  price: number;
  status: 'active' | 'inactive';
  createdAt: string;
}

// Use with generics
const products = baasix.items<Product>('products');

const { data } = await products.find({
  filter: { status: { eq: 'active' } }, // Type-safe!
});

// data is Product[]

On this page