Additional Features

Quick reference for Svelar's supporting modules. Each section covers configuration and common usage patterns.

For in-depth guides, see the dedicated pages: Sessions, Events, Mail, Broadcasting, Storage, PDF, Excel, Feature Flags.

Hashing

Securely hash passwords and other sensitive data.

Configuration

import { Hash } from '@beeblock/svelar/hashing';

Hash.configure({
  driver: 'scrypt',  // 'bcrypt', 'argon2'
});

Hashing Passwords

import { Hash } from '@beeblock/svelar/hashing';

// Hash password
const hashedPassword = await Hash.make('user-password');

// Verify password
const isValid = await Hash.verify('user-password', hashedPassword);

if (isValid) {
  console.log('Password correct');
} else {
  console.log('Password incorrect');
}

Drivers

  • scrypt (default) - Zero dependencies, fast, secure
  • bcrypt - Battle-tested, slower
  • argon2 - Modern, memory-hard

Caching

Cache frequently accessed data to improve performance.

Configuration

import { Cache } from '@beeblock/svelar/cache';

Cache.configure({
  default: 'memory',
  stores: {
    memory: {
      driver: 'memory',
      ttl: 3600,
    },
    file: {
      driver: 'file',
      path: './storage/cache',
      ttl: 3600,
    },
    redis: {
      driver: 'redis',
      host: 'localhost',
      port: 6379,
      password: process.env.REDIS_PASSWORD,
      db: 0,
      prefix: 'svelar_cache:',
      ttl: 3600,
    },
  },
});

The store-level ttl is the default TTL in seconds. It applies to Cache.put() and direct store calls such as Cache.store('redis').put() unless you pass a TTL for that call.

File cache treats missing, expired, and corrupted entries as cache misses and removes corrupted/expired files. Filesystem errors that are not normal cache misses are not swallowed.

Using Cache

import { Cache } from '@beeblock/svelar/cache';

// Set cache
await Cache.put('user:1:posts', posts, 3600);  // 1 hour

// Get cache
const cachedPosts = await Cache.get('user:1:posts');

// Get or set
const posts = await Cache.remember('user:1:posts', 3600, async () => {
  return await Post.where('user_id', 1).get();
});

// Delete cache
await Cache.forget('user:1:posts');

// Clear all cache
await Cache.flush();

// Check existence
const exists = await Cache.has('user:1:posts');

Logging

Log messages for debugging and monitoring.

Configuration

import { Log } from '@beeblock/svelar/logging';

Log.configure({
  default: 'stack',
  channels: {
    console: {
      driver: 'console',
      level: 'debug',
    },
    file: {
      driver: 'file',
      path: './storage/logs/app.log',
      level: 'info',
    },
  },
});

Using Logs

import { Log } from '@beeblock/svelar/logging';

// Log levels
await Log.debug('Debug message');
await Log.info('Information message');
await Log.warn('Warning message');
await Log.error('Error message');

// With context
await Log.info('User registered', { user_id: 1, email: 'john@example.com' });

// Multiple channels
await Log.channel('file').info('Logged to file only');
await Log.channel('console').warn('Logged to console only');

Log writes return promises. Await file and stack channels when you need to guarantee persistence or surface write failures. Missing channels, unknown drivers, and file write errors throw instead of falling back to console.

Notifications

Send notifications via email, database, and custom channels.

Configuration

import { Notifier } from '@beeblock/svelar/notifications';

Notifier.configure({
  channels: {
    email: {
      driver: 'email',
    },
    database: {
      driver: 'database',
      table: 'notifications',
    },
  },
});

Notification Classes

import { Notification } from '@beeblock/svelar/notifications';

export class OrderShippedNotification extends Notification {
  constructor(private order: Order) {
    super();
  }

  channels() {
    return ['email', 'database'];
  }

  toEmail() {
    return {
      to: this.order.user.email,
      subject: 'Your order has shipped!',
      html: `<p>Order #${this.order.id} is on its way.</p>`,
    };
  }

  toDatabase() {
    return {
      type: 'order_shipped',
      title: 'Order Shipped',
      message: `Your order #${this.order.id} has shipped`,
      data: { order_id: this.order.id },
    };
  }
}

// Send
await Notifier.notify(user, new OrderShippedNotification(order));

Register custom channels with Notifier.extend():

Notifier.extend('audit', {
  async send(notifiable, notification) {
    const payload = notification.toChannel?.('audit', notifiable);
    await auditService.record(payload);
  },
});

Configuration Management

Svelar uses a directory-based configuration system like Laravel. Each concern gets its own file in a config/ directory at your project root.

Generating Config Files

npx svelar make:config app
npx svelar make:config database
npx svelar make:config auth
npx svelar make:config mail
npx svelar make:config cache
npx svelar make:config queue
npx svelar make:config storage
npx svelar make:config logging

Config Files

Config files live in config/ and export a default object. Use the env() helper to read environment variables with type casting and defaults:

// config/app.ts
import { env } from '@beeblock/svelar/config';

export default {
  name: env('APP_NAME', 'Svelar'),
  env: env('APP_ENV', 'development'),
  debug: env<boolean>('APP_DEBUG', false),
  url: env('APP_URL', 'http://localhost:5173'),
  key: env('APP_KEY', ''),
  timezone: 'UTC',
  locale: 'en',
};

Loading Configuration

import { config } from '@beeblock/svelar/config';

await config.loadFromDirectory('./config');

Scaffolded apps call config.loadFromDirectory('config') in src/app.ts, so generated config files are loaded automatically when the web server or a Svelar CLI runtime command boots the application.

Missing config directories are treated as optional and return an empty list. Files inside an existing config directory must import successfully and export an object; syntax errors, failed imports, and invalid exports throw during bootstrap instead of being skipped.

Using Configuration

import { config } from '@beeblock/svelar/config';

const appName = config.get('app.name');
const dbDriver = config.get('database.default');
const debug = config.get('app.debug', false);

config.set('app.maintenance', true);

The env() Helper

import { env } from '@beeblock/svelar/config';

env('APP_NAME')                    // string (default: '')
env('APP_NAME', 'Svelar')         // string with default
env<number>('DB_PORT', 5432)      // auto-casts '5432' → 5432
env<boolean>('APP_DEBUG', false)  // auto-casts 'true' → true

Container & Service Providers

The IoC container manages application dependencies.

Service Providers

import { ServiceProvider } from '@beeblock/svelar/container';

export class AnalyticsProvider extends ServiceProvider {
  register() {
    this.app.singleton('analytics', () => new AnalyticsService());
  }

  boot() {
    const analytics = this.app.make('analytics');
    console.log('Analytics service booted');
  }
}

Container Usage

import { container } from '@beeblock/svelar/container';

container.bind('pdf', () => new PdfGenerator());
container.singleton('auth', () => new AuthManager());

const pdf = container.make('pdf');
const auth = container.make('auth');

Error Handling

Handle errors gracefully across your application.

import { abort, abortIf, abortUnless, ModelNotFoundError } from '@beeblock/svelar/errors';

abort(404, 'Resource not found');
abortIf(!user, 401, 'Unauthenticated');
abortUnless(isAdmin, 403, 'Forbidden');

See Error Handling for the full guide.

UUIDv7 & ULID

Svelar provides built-in generators for modern, time-sortable identifiers. Both are available from @beeblock/svelar/support.

UUIDv7

import { uuidv7, isUuidv7, uuidv7Timestamp } from '@beeblock/svelar/support';

const id = uuidv7();
// → '019503a4-6b2c-7a1e-8f3d-4a2b1c0d9e8f'

isUuidv7(id);         // → true
uuidv7Timestamp(id);  // → Date object

ULID

import { ulid, isUlid, ulidTimestamp } from '@beeblock/svelar/support';

const id = ulid();
// → '01ARYZ6S41TSV4RRFFQ69G5FAV'

isUlid(id);         // → true
ulidTimestamp(id);   // → Date object

JSONB Columns

Use jsonb() in migrations for explicit JSONB storage on PostgreSQL. On MySQL it maps to JSON, on SQLite it falls back to TEXT.

Custom CLI Commands

Create your own CLI commands:

npx svelar make:command SyncUsers
import { Command } from '@beeblock/svelar/cli';

export class SyncUsersCommand extends Command {
  name = 'app:sync-users';
  description = 'Sync users from external API';
  arguments = ['source'];
  flags = [
    { name: 'force', alias: 'f', description: 'Force full sync', type: 'boolean' as const },
    { name: 'limit', description: 'Max users to sync', type: 'string' as const, default: '100' },
  ];

  async handle(args: string[], flags: Record<string, any>): Promise<void> {
    const source = args[0] ?? 'default';
    await this.bootstrap();
    this.info(`Syncing users from ${source}...`);
    this.success(`Done.`);
  }
}

Commands are auto-discovered from src/lib/commands/ in flat projects and src/lib/shared/commands/ in DDD projects — no registration needed.

Admin Dashboard

Scaffold a production-ready admin dashboard:

npx svelar make:dashboard

This creates API routes (/api/admin/*) and a dashboard page with system health, queue monitoring, scheduler management, and log viewing.

Generated admin API routes require event.locals.user.role === 'admin' by default. Adjust the generated check if your app uses a different admin contract.

import { configureDashboard } from '@beeblock/svelar/dashboard';
import { JobMonitor } from '@beeblock/svelar/queue/JobMonitor';
import { ScheduleMonitor } from '@beeblock/svelar/scheduler/ScheduleMonitor';

configureDashboard({ enabled: true, prefix: '/admin' });

When the dashboard plugin is booted with background health collection enabled, it records the latest health snapshot plus last run/success/error metadata. Health collection failures are exposed through dashboard data instead of being logged and forgotten.

Method Endpoint Description
GET /api/admin/health System health
GET /api/admin/queue List jobs
POST /api/admin/queue/[id]/retry Retry failed job
GET /api/admin/scheduler List scheduled tasks
POST /api/admin/scheduler/[name]/run Run task manually
GET /api/admin/logs Query logs
GET /api/admin/stats Combined dashboard stats

Plugin System

Plugins extend Svelar with new capabilities:

import { PluginManager } from '@beeblock/svelar/plugins';

const plugins = new PluginManager(app);
plugins.use(new StripePlugin());
await plugins.boot();

Create plugins by extending the Plugin base class with register(), boot(), migrations(), and config() lifecycle hooks.

Audit Logging

Track user actions and system changes:

import { Audit } from '@beeblock/svelar/audit';

await Audit.log('user:created', {
  userId: user.id,
  email: user.email,
  ipAddress: event.getClientAddress(),
});

const entries = await Audit.query()
  .where('action', 'user:created')
  .limit(10)
  .get();

API Key Management

Secure token-based authentication:

import { ApiKeys } from '@beeblock/svelar/api-keys';

const { plainTextKey, record } = await ApiKeys.create({
  name: 'My Integration',
  userId: user.id,
  permissions: ['users:read', 'posts:write'],
});

Keys are hashed and can be revoked at any time. Permissions are deny-by-default: an empty permission list authenticates the key owner but does not pass permission checks. Use '*' only for deliberately unrestricted keys.

Outgoing Webhooks

Send events to external services:

import { Webhooks } from '@beeblock/svelar/webhooks';

await Webhooks.register({
  url: 'https://example.com/events',
  events: ['user:created', 'order:shipped'],
  active: true,
});

await Webhooks.dispatch('user:created', { id: user.id, email: user.email });

Events are signed with HMAC and retried automatically.

Teams & Workspaces

Multi-tenant team management:

import { Teams } from '@beeblock/svelar/teams';

const team = await Teams.create({
  name: 'Acme Corp',
  ownerId: user.id,
});

await Teams.invite(team.id, 'member@example.com', 'admin');

Tables are managed by Svelar core migrations. Teams use owner, admin, member, and viewer roles by default; pass roles to Teams.configure() to allow custom roles. The owner role is reserved for the team owner, cannot be assigned through invitations or member updates, and the owner member cannot be removed through removeMember().

Email Templates

Manage and render email templates:

import { EmailTemplates } from '@beeblock/svelar/email-templates';

await EmailTemplates.register({
  name: 'welcome',
  subject: 'Welcome to {{ appName }}',
  html: `<p>Hello {{ userName }},</p>...`,
  variables: ['appName', 'userName'],
  active: true,
});

const { html } = await EmailTemplates.render('welcome', {
  appName: 'My App',
  userName: user.name,
});

File Uploads

Track and serve user-uploaded files:

import { Uploads } from '@beeblock/svelar/uploads';

const upload = await Uploads.store(formFile, {
  disk: 'local',
  directory: `users/${user.id}`,
  userId: user.id,
});

const url = await Uploads.getUrl(upload.id, 3600);

Uploads.store(), Uploads.delete(), and Uploads.getUrl() use the configured Storage disk and throw storage configuration, filesystem, or S3/RustFS errors. Upload metadata is only created after the file write succeeds, and metadata is not deleted when file deletion fails.

Billing with Stripe

Stripe billing is available as the @beeblock/svelar-stripe plugin. Install the Stripe SDK and configure:

npm install @beeblock/svelar-stripe stripe
npx svelar plugin:install @beeblock/svelar-stripe
// src/app.ts
import { Stripe } from '@beeblock/svelar-stripe';

Stripe.configure({
  secretKey: process.env.STRIPE_SECRET_KEY ?? '',
  publishableKey: process.env.STRIPE_PUBLISHABLE_KEY ?? '',
  webhookSecret: process.env.STRIPE_WEBHOOK_SECRET ?? '',
  currency: 'usd',
});
// Create a subscription
const subscription = await Stripe.service().createSubscription(
  user.stripe_customer_id,
  'price_xxxxx',
  { trialDays: 14 },
);

// Cancel at end of billing period
await Stripe.service().cancelSubscription(subscription.id, false);

// Handle webhooks
Stripe.webhooks()
  .on('invoice.payment_succeeded', async (event) => {
    // Record payment
  });

The scaffold includes a billing page at /dashboard/billing, admin billing management, and a webhook route at /api/webhooks/stripe.

See Stripe Billing for the full guide: products, prices, currencies, checkout, portal, invoices, refunds, and webhook events.


Svelar © 2026 · MIT License