BaasixBaasix

Services Reference

Baasix provides a comprehensive set of services that can be used in your extensions, hooks, and custom endpoints. All services are exported from the main @tspvivek/baasix package.

Table of Contents

  1. ItemsService - Core CRUD operations
  2. FilesService - File upload and management
  3. AssetsService - Image processing and asset management
  4. MailService - Email sending
  5. NotificationService - In-app notifications
  6. StorageService - File storage providers
  7. SettingsService - Application settings
  8. PermissionService - Role-based access control
  9. CacheService - Caching with multiple adapters
  10. HooksManager - Lifecycle hooks
  11. TasksService - Scheduled tasks
  12. WorkflowService - Workflow execution
  13. SocketService - Real-time WebSocket
  14. ReportService - Report generation
  15. StatsService - Statistics aggregation

Importing Services

import {
  ItemsService,
  FilesService,
  AssetsService,
  MailService,
  NotificationService,
  StorageService,
  SettingsService,
  PermissionService,
  HooksManager,
  TasksService,
  WorkflowService,
  SocketService,
  ReportService,
  StatsService,
  getCacheService,
} from '@tspvivek/baasix';

ItemsService

The core service for all CRUD operations on collections. Handles permissions, multi-tenancy, relations, hooks, and caching.

Constructor

new ItemsService(collection: string, params?: ServiceParams)

Parameters:

ParameterTypeDescription
collectionstringThe collection/table name
params.accountabilityobjectUser context with user, role, permissions, tenant
params.tenantstring | numberTenant ID for multi-tenant operations

Methods

readByQuery

Read multiple records with filtering, sorting, pagination, and relations.

async readByQuery(
  query: QueryOptions,
  skipCount?: boolean
): Promise<ReadResult>

Query Options:

OptionTypeDescription
filterobjectFilter conditions using operators
fieldsstring[]Fields to select (supports dot notation for relations)
sortstring[] | objectSort order (prefix with - for descending)
limitnumberNumber of records to return (-1 for all)
pagenumberPage number for pagination
offsetnumberNumber of records to skip
searchstringFull-text search query
searchFieldsstring[]Fields to search in
paranoidbooleanInclude soft-deleted records when false
relConditionsobjectDeep filter for array relations (O2M/M2M) - filters what items appear in the returned arrays. See relConditions docs
aggregateobjectAggregation operations
groupBystring[]Group by fields

Example:

const itemsService = new ItemsService('posts', {
  accountability: req.accountability,
});

// Basic query with filter and sort
const result = await itemsService.readByQuery({
  filter: { status: { eq: 'published' } },
  fields: ['id', 'title', 'author.name', 'category.name'],
  sort: ['-createdAt'],
  limit: 10,
  page: 1,
});

// With search and relConditions (for HasMany relations like comments)
const searchResult = await itemsService.readByQuery({
  search: 'typescript',
  searchFields: ['title', 'content'],
  fields: ['*', 'comments.*'],
  relConditions: {
    comments: { isApproved: { eq: true } }, // Only load approved comments
  },
});

console.log(result.data); // Array of records
console.log(result.totalCount); // Total matching records

readOne

Read a single record by ID.

async readOne(
  id: string | number,
  query?: QueryOptions
): Promise<any>

Example:

const post = await itemsService.readOne('post-uuid', {
  fields: ['*', 'author.*', 'comments.*'],
});

createOne

Create a single record.

async createOne(
  data: Record<string, any>,
  options?: OperationOptions
): Promise<string | number>

Options:

OptionTypeDescription
bypassPermissionsbooleanSkip permission checks
bypassHooksbooleanSkip lifecycle hooks

Example:

const postId = await itemsService.createOne({
  title: 'New Post',
  content: 'Post content...',
  status: 'draft',
  author_Id: req.accountability.user.id,
});

createMany

Create multiple records.

async createMany(
  data: Record<string, any>[],
  options?: OperationOptions
): Promise<(string | number)[]>

Example:

const ids = await itemsService.createMany([
  { title: 'Post 1', status: 'draft' },
  { title: 'Post 2', status: 'published' },
]);

updateOne

Update a single record by ID.

async updateOne(
  id: string | number,
  data: Record<string, any>,
  options?: OperationOptions
): Promise<string | number>

Example:

await itemsService.updateOne('post-uuid', {
  title: 'Updated Title',
  status: 'published',
});

updateMany

Update multiple records. Accepts an array of update objects, each containing an id and the fields to update.

async updateMany(
  updates: { id: string | number; data?: Record<string, any>; [key: string]: any }[],
  options?: OperationOptions
): Promise<(string | number)[]>

Example:

// Update with individual data per item
const updatedIds = await itemsService.updateMany([
  { id: 'uuid-1', data: { status: 'published', priority: 1 } },
  { id: 'uuid-2', data: { status: 'draft', priority: 2 } },
]);

// Alternative: spread fields directly (without data wrapper)
const updatedIds = await itemsService.updateMany([
  { id: 'uuid-1', status: 'published', priority: 1 },
  { id: 'uuid-2', status: 'draft', priority: 2 },
]);

updateByQuery

Update records matching a filter.

async updateByQuery(
  filter: object,
  data: Record<string, any>,
  options?: OperationOptions
): Promise<number>

Example:

const count = await itemsService.updateByQuery(
  { status: { eq: 'draft' }, createdAt: { lt: '2024-01-01' } },
  { status: 'archived' },
);

deleteOne

Delete a single record by ID.

async deleteOne(
  id: string | number,
  options?: OperationOptions
): Promise<string | number>

deleteMany

Delete multiple records by IDs.

async deleteMany(
  ids: (string | number)[],
  options?: OperationOptions
): Promise<(string | number)[]>

softDelete / restore

Soft delete and restore records (for collections with soft delete enabled).

async softDelete(id: string | number): Promise<void>
async restore(id: string | number): Promise<void>

FilesService

Handles file uploads, storage, and metadata extraction.

Constructor

new FilesService(params?: { accountability?: any })

Methods

createOne

Upload a file and create a file record.

async createOne(
  fileData: { file: UploadedFile },
  metadata?: FileMetadata
): Promise<string | number>

FileMetadata:

FieldTypeDescription
titlestringDisplay title
namestringFilename
descriptionstringFile description
storagestringStorage provider name
folderstringVirtual folder path
isPublicbooleanPublic access flag

Example:

const filesService = new FilesService({
  accountability: req.accountability,
});

const fileId = await filesService.createOne(
  { file: req.files.upload },
  {
    title: 'Profile Photo',
    storage: 'local',
    isPublic: true,
  },
);

readOne

Get file details and signed URL.

async readOne(id: string | number): Promise<FileRecord>

deleteOne

Delete a file and its storage.

async deleteOne(id: string | number): Promise<void>

downloadFromUrl

Download a file from URL and store it.

async downloadFromUrl(
  url: string,
  metadata?: FileMetadata
): Promise<string | number>

MailService

Send emails using configured SMTP providers with template support.

Methods

sendMail

Send an email using a template.

async sendMail(options: MailOptions): Promise<any>

MailOptions:

FieldTypeDescription
tostringRecipient email
subjectstringEmail subject
templateNamestringTemplate name (without .liquid)
contextobjectTemplate variables
fromstringOverride sender address
senderstringSender provider name
attachmentsarrayEmail attachments
tenantIdstring | numberTenant for tenant-specific SMTP

Example:

import { MailService } from '@tspvivek/baasix';

await MailService.sendMail({
  to: 'user@example.com',
  subject: 'Welcome to Our App',
  templateName: 'welcome',
  context: {
    userName: 'John',
    activationLink: 'https://app.example.com/activate/token123',
  },
});

Custom Templates

Place custom email templates in extensions/baasix-templates/mails/:

<!-- extensions/baasix-templates/mails/welcome.liquid -->
<html>
  <body>
    <h1>Welcome, {{ userName }}!</h1>
    <p>Click below to activate your account:</p>
    <a href="{{ activationLink }}">Activate Account</a>
  </body>
</html>

NotificationService

Send and manage in-app notifications for users.

Constructor

new NotificationService(params?: { accountability?: any })

Methods

send

Send notifications to multiple users.

async send(options: NotificationOptions): Promise<(string | number)[]>

NotificationOptions:

FieldTypeDescription
typestringNotification type
titlestringNotification title
messagestringNotification message
dataobjectAdditional data payload
userIdsstring[]Array of user IDs
tenant_Idstring | numberTenant ID

Example:

const notificationService = new NotificationService({
  accountability: req.accountability,
});

await notificationService.send({
  type: 'info',
  title: 'New Comment',
  message: 'Someone commented on your post',
  data: { postId: 'post-123' },
  userIds: ['user-1', 'user-2'],
});

markAsSeen

Mark notifications as seen.

async markAsSeen(
  userId: string,
  notificationIds?: string[] | null
): Promise<number>

getUnreadCount

Get count of unread notifications.

async getUnreadCount(userId: string): Promise<number>

getUserNotifications

Get user's notifications with pagination.

async getUserNotifications(query?: QueryOptions): Promise<ReadResult>

deleteForUser

Delete user's notifications.

async deleteForUser(
  userId: string,
  notificationIds?: string[] | null
): Promise<number>

StorageService

Manage file storage with multiple provider support (Local, S3).

Methods

getProvider

Get a configured storage provider.

getProvider(service: string): StorageProvider

saveFile

Save a file to storage.

async saveFile(
  service: string,
  filePath: string,
  fileContent: Buffer
): Promise<string>

getFile

Retrieve a file from storage.

async getFile(service: string, filePath: string): Promise<Buffer>

deleteFile

Delete a file from storage.

async deleteFile(service: string, filePath: string): Promise<void>

getPublicUrl

Get a public/signed URL for a file.

async getPublicUrl(service: string, filePath: string): Promise<string>

Example:

import { StorageService } from '@tspvivek/baasix';

// Save a file
const filePath = await StorageService.saveFile('local', 'documents/report.pdf', fileBuffer);

// Get signed URL (for S3)
const url = await StorageService.getPublicUrl('s3', 'documents/report.pdf');

SettingsService

Manage global and tenant-specific application settings.

Methods

getGlobalSettings

Get global application settings.

getGlobalSettings(): TenantSettings

getTenantSettings

Get merged settings for a tenant (global + tenant overrides).

async getTenantSettings(tenantId: string | number): Promise<TenantSettings>

updateGlobalSettings

Update global settings.

async updateGlobalSettings(
  data: Partial<TenantSettings>,
  accountability?: any
): Promise<TenantSettings>

updateTenantSettings

Update tenant-specific settings.

async updateTenantSettings(
  tenantId: string | number,
  data: Partial<TenantSettings>,
  accountability?: any
): Promise<TenantSettings>

Example:

import { SettingsService } from '@tspvivek/baasix';

// Get global settings
const settings = SettingsService.getGlobalSettings();
console.log(settings.project_name);

// Get tenant-specific settings (merged with global)
const tenantSettings = await SettingsService.getTenantSettings('tenant-123');

CacheService

High-performance caching with multiple adapters (InMemory, Redis, Upstash).

Getting the Cache Service

import { getCacheService } from '@tspvivek/baasix';

const cache = getCacheService();

Methods

get

Get a cached value.

async get(key: string): Promise<any | null>

set

Set a cached value with TTL.

async set(
  key: string,
  value: any,
  ttl?: number
): Promise<void>

delete

Delete a cached value.

async delete(key: string): Promise<void>

invalidateCollection

Invalidate all cache entries for a collection.

import { invalidateCollection } from '@tspvivek/baasix';

await invalidateCollection('posts');

invalidateEntireCache

Clear all cache entries.

import { invalidateEntireCache } from '@tspvivek/baasix';

await invalidateEntireCache();

Example:

const cache = getCacheService();

// Cache a computed value
await cache.set('dashboard:stats', computedStats, 3600); // 1 hour TTL

// Get cached value
const stats = await cache.get('dashboard:stats');

// Invalidate on data change
await invalidateCollection('orders');

HooksManager

Register and execute lifecycle hooks for collections.

Methods

registerHook

Register a hook for a collection event.

registerHook(
  collection: string,
  event: string,
  hookFunction: HookFunction
): void

Events:

  • items.create.before / items.create.after
  • items.update.before / items.update.after
  • items.delete.before / items.delete.after
  • items.read.before / items.read.after

HookFunction Signature:

type HookFunction = (context: HookContext) => Promise<HookContext | void>;

interface HookContext {
  collection: string;
  accountability: any;
  db: Database;
  data?: Record<string, any>; // For create/update
  document?: Record<string, any>; // After operations
  id?: string | number; // For update/delete
  ids?: (string | number)[]; // For bulk operations
}

Example:

import { HooksManager } from '@tspvivek/baasix';

// Register in extensions/baasix-hook-posts/index.js
export default function (hooksManager, context) {
  // Before creating a post
  hooksManager.registerHook('posts', 'items.create.before', async (ctx) => {
    // Add slug from title
    if (ctx.data.title) {
      ctx.data.slug = ctx.data.title.toLowerCase().replace(/\s+/g, '-');
    }
    return ctx;
  });

  // After creating a post
  hooksManager.registerHook('posts', 'items.create.after', async (ctx) => {
    // Send notification
    console.log('Post created:', ctx.document.id);
  });

  // Wildcard hook for all collections
  hooksManager.registerHook('*', 'items.create.after', async (ctx) => {
    console.log(`Record created in ${ctx.collection}`);
  });
}

TasksService

Manage and process scheduled tasks with distributed locking support for multi-instance deployments.

Configuration

# Enable task service
TASK_SERVICE_ENABLED=true

# For multi-instance deployments
TASK_REDIS_ENABLED=true
TASK_REDIS_URL=redis://localhost:6379

Methods

getNotStartedTasks

Get tasks that are pending execution.

async getNotStartedTasks(): Promise<Task[]>

tryAcquireLock

Acquire distributed lock for task processing. In multi-instance mode, uses Redis SETNX for atomic locking.

async tryAcquireLock(lockTimeout?: number): Promise<boolean>
  • lockTimeout: Lock expiration in seconds (default: 60)
  • Returns: true if lock acquired, false if already held

releaseLock

Release the distributed lock. Only releases if the current instance owns the lock.

async releaseLock(): Promise<boolean>

getCacheStats

Get task service statistics including distributed mode status.

async getCacheStats(): Promise<{
  cachedTasksCount: number;
  isTaskRunning: boolean;
  refreshInterval: number;
  initialized: boolean;
  distributedMode: boolean;  // true when TASK_REDIS_ENABLED
  instanceId: string;
}>

Example Task Processor Extension:

// extensions/baasix-schedule-task-processor/index.js
import { TasksService, ItemsService } from '@tspvivek/baasix';

const tasksService = new TasksService();

export default async function (hooksManager, context) {
  // Run every minute
  setInterval(async () => {
    // Try to acquire lock (only one instance processes)
    const hasLock = await tasksService.tryAcquireLock(60);
    if (!hasLock) return;

    try {
      const tasks = await tasksService.getNotStartedTasks();

      for (const task of tasks) {
        if (new Date(task.scheduled_time) <= new Date()) {
          // Process task
          await processTask(task);
        }
      }
    } finally {
      await tasksService.releaseLock();
    }
  }, 60000);
}

WorkflowService

Execute visual workflows with various node types.

Methods

executeWorkflow

Execute a workflow by ID.

async executeWorkflow(
  workflowId: string | number,
  triggerData?: any,
  userId?: any,
  tenantId?: any
): Promise<WorkflowExecution>

registerCustomModule

Register a custom module for use in Script nodes.

registerCustomModule(
  moduleName: string,
  moduleExport: any,
  options?: { description?: string }
): void

Example:

import { WorkflowService } from '@tspvivek/baasix';

// Register custom module
WorkflowService.registerCustomModule(
  'myUtils',
  {
    formatCurrency: (amount) => `$${amount.toFixed(2)}`,
    calculateTax: (amount, rate) => amount * rate,
  },
  {
    description: 'Custom utility functions',
  },
);

// Execute workflow
const execution = await WorkflowService.executeWorkflow(
  'workflow-uuid',
  { orderId: 'order-123' },
  req.accountability.user.id,
);

SocketService

Real-time WebSocket communication with Socket.IO.

Methods

broadcastChange

Broadcast a change to subscribed clients.

broadcastChange(
  collection: string,
  action: 'create' | 'update' | 'delete',
  data: any,
  accountability?: any
): void

emitToUser

Send event to a specific user.

emitToUser(
  userId: string | number,
  event: string,
  data: any
): void

emitToTenant

Send event to all users in a tenant.

emitToTenant(
  tenantId: string | number,
  event: string,
  data: any
): void

Example:

import { SocketService } from '@tspvivek/baasix';

// Broadcast custom event
SocketService.emitToUser('user-123', 'order:shipped', {
  orderId: 'order-456',
  trackingNumber: 'TRACK123',
});

// Broadcast to tenant
SocketService.emitToTenant('tenant-789', 'announcement', {
  message: 'System maintenance scheduled',
});

ReportService

Generate reports with aggregations and relations.

Constructor

new ReportService(
  collection: string,
  params?: { accountability?: any }
)

Methods

generateReport

Generate a report with aggregations.

async generateReport(query: ReportQuery): Promise<ReadResult>

ReportQuery:

FieldTypeDescription
fieldsstring[]Fields to include
filterobjectFilter conditions
aggregateobjectAggregation functions
groupBystring[]Group by fields
sortstring[]Sort order
limitnumberResult limit

Example:

const reportService = new ReportService('orders', {
  accountability: req.accountability,
});

const report = await reportService.generateReport({
  fields: ['status', 'customer.name'],
  filter: { createdAt: { gte: '2024-01-01' } },
  aggregate: {
    count: '*',
    sum: ['total'],
    avg: ['total'],
  },
  groupBy: ['status'],
});

StatsService

Execute multiple stat queries in parallel.

Constructor

new StatsService(params?: { accountability?: any })

Methods

generateStats

Execute multiple stat queries.

async generateStats(queries: StatsQuery[]): Promise<StatsResult>

Example:

const statsService = new StatsService({
  accountability: req.accountability,
});

const stats = await statsService.generateStats([
  {
    name: 'totalOrders',
    collection: 'orders',
    query: { aggregate: { count: '*' } },
  },
  {
    name: 'revenueByMonth',
    collection: 'orders',
    query: {
      fields: ['createdAt'],
      aggregate: { sum: ['total'] },
      groupBy: ['month(createdAt)'],
    },
  },
  {
    name: 'topProducts',
    collection: 'order_items',
    query: {
      fields: ['product.name'],
      aggregate: { sum: ['quantity'] },
      groupBy: ['product_Id'],
      sort: ['-sum_quantity'],
      limit: 10,
    },
  },
]);

console.log(stats.data.totalOrders);
console.log(stats.data.revenueByMonth);
console.log(stats.data.topProducts);

AssetsService

Image processing and asset management service with caching support. Extends FilesService to provide image transformations.

Constructor

new AssetsService(params?: { accountability?: any })

Methods

getAsset

Get an asset with optional transformations (resize, quality adjustment).

async getAsset(
  id: string | number,
  query: AssetQuery,
  bypassPermissions?: boolean
): Promise<AssetResult>

AssetQuery:

OptionTypeDescription
widthnumberTarget width in pixels
heightnumberTarget height in pixels
fitstringResize fit mode: 'cover', 'contain', 'fill', 'inside', 'outside'
qualitynumberJPEG quality (1-100)
withoutEnlargementbooleanPrevent upscaling

AssetResult:

FieldTypeDescription
bufferBuffer | nullFile content buffer
contentTypestringMIME type
filePathstring | nullLocal file path (for streaming)
fileobjectFile record from database
isS3booleanWhether file is stored in S3

Example:

import { AssetsService } from '@tspvivek/baasix';

const assetsService = new AssetsService({
  accountability: req.accountability,
});

// Get resized image
const asset = await assetsService.getAsset('file-uuid', {
  width: 300,
  height: 300,
  fit: 'cover',
  quality: 80,
});

// Set response headers
res.setHeader('Content-Type', asset.contentType);
res.send(asset.buffer);

processImage

Process an image with transformations.

async processImage(
  filePath: string,
  options: AssetQuery
): Promise<ProcessedImage>

Example:

const processed = await assetsService.processImage('/path/to/image.jpg', {
  width: 800,
  height: 600,
  fit: 'contain',
  quality: 85,
});

// processed.buffer contains the transformed image
// processed.contentType is 'image/jpeg'

clearCache

Delete processed versions of an image when the original is deleted.

async deleteProcessedVersions(file: any): Promise<void>

PermissionService

Role-based access control and permission checking.

Methods

canAccess

Check if a role has access to perform an action on a collection.

async canAccess(
  roleId: string | number,
  collection: string,
  action: string,
  fields?: string[]
): Promise<boolean>

Example:

import PermissionService from '@tspvivek/baasix';

const hasAccess = await PermissionService.canAccess(roleId, 'posts', 'update', ['title', 'content']);

if (!hasAccess) {
  throw new APIError('Permission denied', 403);
}

getPermissions

Get all permissions for a role.

async getPermissions(roleId: string | number): Promise<Permission[]>

getCollectionPermissions

Get permissions for a specific collection.

async getCollectionPermissions(
  roleId: string | number,
  collection: string
): Promise<Permission[]>

On this page