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
- ItemsService - Core CRUD operations
- FilesService - File upload and management
- AssetsService - Image processing and asset management
- MailService - Email sending
- NotificationService - In-app notifications
- StorageService - File storage providers
- SettingsService - Application settings
- PermissionService - Role-based access control
- CacheService - Caching with multiple adapters
- HooksManager - Lifecycle hooks
- TasksService - Scheduled tasks
- WorkflowService - Workflow execution
- SocketService - Real-time WebSocket
- ReportService - Report generation
- 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:
| Parameter | Type | Description |
|---|---|---|
| collection | string | The collection/table name |
| params.accountability | object | User context with user, role, permissions, tenant |
| params.tenant | string | number | Tenant 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:
| Option | Type | Description |
|---|---|---|
| filter | object | Filter conditions using operators |
| fields | string[] | Fields to select (supports dot notation for relations) |
| sort | string[] | object | Sort order (prefix with - for descending) |
| limit | number | Number of records to return (-1 for all) |
| page | number | Page number for pagination |
| offset | number | Number of records to skip |
| search | string | Full-text search query |
| searchFields | string[] | Fields to search in |
| paranoid | boolean | Include soft-deleted records when false |
| relConditions | object | Deep filter for array relations (O2M/M2M) - filters what items appear in the returned arrays. See relConditions docs |
| aggregate | object | Aggregation operations |
| groupBy | string[] | 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 recordsreadOne
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:
| Option | Type | Description |
|---|---|---|
| bypassPermissions | boolean | Skip permission checks |
| bypassHooks | boolean | Skip 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:
| Field | Type | Description |
|---|---|---|
| title | string | Display title |
| name | string | Filename |
| description | string | File description |
| storage | string | Storage provider name |
| folder | string | Virtual folder path |
| isPublic | boolean | Public 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:
| Field | Type | Description |
|---|---|---|
| to | string | Recipient email |
| subject | string | Email subject |
| templateName | string | Template name (without .liquid) |
| context | object | Template variables |
| from | string | Override sender address |
| sender | string | Sender provider name |
| attachments | array | Email attachments |
| tenantId | string | number | Tenant 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:
| Field | Type | Description |
|---|---|---|
| type | string | Notification type |
| title | string | Notification title |
| message | string | Notification message |
| data | object | Additional data payload |
| userIds | string[] | Array of user IDs |
| tenant_Id | string | number | Tenant 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): StorageProvidersaveFile
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(): TenantSettingsgetTenantSettings
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
): voidEvents:
items.create.before/items.create.afteritems.update.before/items.update.afteritems.delete.before/items.delete.afteritems.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:6379Methods
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:
trueif lock acquired,falseif 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 }
): voidExample:
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
): voidemitToUser
Send event to a specific user.
emitToUser(
userId: string | number,
event: string,
data: any
): voidemitToTenant
Send event to all users in a tenant.
emitToTenant(
tenantId: string | number,
event: string,
data: any
): voidExample:
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:
| Field | Type | Description |
|---|---|---|
| fields | string[] | Fields to include |
| filter | object | Filter conditions |
| aggregate | object | Aggregation functions |
| groupBy | string[] | Group by fields |
| sort | string[] | Sort order |
| limit | number | Result 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:
| Option | Type | Description |
|---|---|---|
| width | number | Target width in pixels |
| height | number | Target height in pixels |
| fit | string | Resize fit mode: 'cover', 'contain', 'fill', 'inside', 'outside' |
| quality | number | JPEG quality (1-100) |
| withoutEnlargement | boolean | Prevent upscaling |
AssetResult:
| Field | Type | Description |
|---|---|---|
| buffer | Buffer | null | File content buffer |
| contentType | string | MIME type |
| filePath | string | null | Local file path (for streaming) |
| file | object | File record from database |
| isS3 | boolean | Whether 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[]>Related Documentation
- Hooks System - Detailed hooks documentation
- Extensions Guide - Creating extensions
- Workflow Routes - Workflow API endpoints
- Socket.IO Integration - Real-time features
- Advanced Query Guide - Query syntax details