Plugins Guide
Plugins Guide
Section titled “Plugins Guide”Learn how to use and create plugins for Veloce.
Table of Contents
Section titled “Table of Contents”Introduction
Section titled “Introduction”Veloce’s plugin system allows you to extend the framework with additional functionality. Plugins can add routes, middleware, modify the application, and integrate with external services.
Plugin Interface
Section titled “Plugin Interface”import type { VeloceTS } from 'veloce-ts';
interface Plugin { name: string; version?: string; dependencies?: string[]; install(app: VeloceTS): void | Promise<void>;}Built-in Plugins
Section titled “Built-in Plugins”Official plugins ship as classes and register with app.usePlugin(new PluginClass(...)) (there is no string-based app.install('name') API).
1. Authentication Plugin
Section titled “1. Authentication Plugin”JWT-based authentication with refresh tokens and user management.
import { VeloceTS, AuthPlugin } from 'veloce-ts';
const app = new VeloceTS();
app.usePlugin(new AuthPlugin({ jwt: { secret: 'your-secret-key', expiresIn: '1h', refreshExpiresIn: '7d', }, userProvider: { findByCredentials: async (username: string, password: string) => { // Your user lookup logic return null; }, findById: async (id: string) => { // Your user lookup logic return null; }, hashPassword: async (password: string) => { return password; // replace with real hash }, verifyPassword: async (password: string, hash: string) => { return password === hash; // replace with real verify }, },}));2. RBAC Plugin
Section titled “2. RBAC Plugin”Role-Based Access Control with hierarchical roles and permissions (Role includes permissions: string[] per role).
import { VeloceTS, RBACPlugin } from 'veloce-ts';
const app = new VeloceTS();
app.usePlugin(new RBACPlugin({ roles: [ { name: 'admin', level: 100, permissions: ['users.create', 'users.read', 'users.update', 'users.delete'], }, { name: 'viewer', level: 20, permissions: ['users.read'], }, ],}));3. GraphQL Plugin
Section titled “3. GraphQL Plugin”Schema generation and Playground—see also the GraphQL guide (experimental areas).
import { VeloceTS, GraphQLPlugin } from 'veloce-ts';
const app = new VeloceTS();
app.usePlugin(new GraphQLPlugin({ path: '/graphql', playground: true,}));4. WebSocket Plugin
Section titled “4. WebSocket Plugin”Real-time WebSocket routes from @WebSocket classes. Not supported on Node.js upgrades (501)—use Bun or Deno; see WebSockets and Limitations & roadmap.
import { VeloceTS, WebSocketPlugin } from 'veloce-ts';
const app = new VeloceTS();
app.usePlugin(new WebSocketPlugin());5. OpenAPI Plugin
Section titled “5. OpenAPI Plugin”OpenAPI 3 spec + Swagger UI from route metadata (merges title / version / description from VeloceTS config when set).
import { VeloceTS, OpenAPIPlugin } from 'veloce-ts';
const app = new VeloceTS({ title: 'My API', version: '1.0.0', description: 'API description',});
app.usePlugin(new OpenAPIPlugin({ path: '/openapi.json', docsPath: '/docs', docs: true,}));Using Authentication & RBAC Decorators
Section titled “Using Authentication & RBAC Decorators”Once you’ve installed the auth and RBAC plugins, you can use the decorators:
import { Controller, Get, Post, Auth, CurrentUser, MinimumRole, Permissions} from 'veloce-ts';
@Controller('/api/users')export class UserController {
// Require authentication @Get('/profile') @Auth() async getProfile(@CurrentUser() user: any) { return user; }
// Require specific role level @Get('/admin-only') @MinimumRole('admin') async adminOnly() { return { message: 'Admin access granted' }; }
// Require specific permissions @Post('/') @Permissions('users.create') async createUser(@Body() userData: any) { return { message: 'User created', user: userData }; }
// Multiple permissions (user needs ALL) @Put('/:id') @Permissions('users.update', 'users.read') async updateUser(@Param('id') id: string, @Body() userData: any) { return { message: 'User updated', id, user: userData }; }}Available Decorators:
@Auth()- Requires valid JWT token@CurrentUser()- Injects current authenticated user@MinimumRole(roleName)- Requires minimum role level@Permissions(...perms)- Requires specific permissions
HealthCheckPlugin
Section titled “HealthCheckPlugin”Registers /health, /ready, and /live. Custom checks are passed as an array via checks.
import { Veloce, HealthCheckPlugin, HealthCheckers } from 'veloce-ts';import { sql } from 'drizzle-orm';
const app = new Veloce();
app.usePlugin(new HealthCheckPlugin({ path: '/health', readyPath: '/ready', livePath: '/live', checks: [ HealthCheckers.database(async () => { await db.execute(sql`SELECT 1`); return true; }), HealthCheckers.disk(process.cwd(), 90), HealthCheckers.memory(512), ],}));Sample response (/health):
{ "status": "healthy", "uptime": 3600, "timestamp": "2026-03-27T12:00:00.000Z", "checks": { "database": { "status": "healthy", "message": "Database connection OK" }, "disk": { "status": "healthy", "message": "Disk usage: 45.0% (threshold: 90%)", "usagePercent": 45 }, "memory": { "status": "healthy", "message": "Heap usage: 62.00MB / 512MB", "heapUsedMB": "62.00", "maxUsageMB": 512 } }}HealthCheckers helpers:
HealthCheckers.alwaysHealthy()— returns a static healthy result (testing)HealthCheckers.database(pingFn)—pingFnreturnsbooleanorPromise<boolean>; unhealthy iffalseor throwsHealthCheckers.memory(maxUsageMB?)— heap vs limit (default 512 MB)HealthCheckers.disk(path?, maxUsagePercent?)— usesfs.statfswhere available (Node 18+ / Bun)
Using Plugins
Section titled “Using Plugins”Basic Usage
Section titled “Basic Usage”import { VeloceTS, OpenAPIPlugin, GraphQLPlugin } from 'veloce-ts';
const app = new VeloceTS();
app.usePlugin(new OpenAPIPlugin());
app.usePlugin(new OpenAPIPlugin());app.usePlugin(new GraphQLPlugin());Multiple plugin instances
Section titled “Multiple plugin instances”VeloceTS does not take a plugins: [] option—register each plugin with usePlugin:
import { VeloceTS, OpenAPIPlugin, GraphQLPlugin } from 'veloce-ts';
const app = new VeloceTS();
app.usePlugin(new OpenAPIPlugin({ path: '/api-docs.json', docsPath: '/api-docs',}));app.usePlugin(new GraphQLPlugin({ path: '/gql', playground: process.env.NODE_ENV !== 'production',}));Creating Custom Plugins
Section titled “Creating Custom Plugins”Simple Plugin
Section titled “Simple Plugin”import { Plugin, VeloceTS } from 'veloce-ts';
class HelloPlugin implements Plugin { name = 'hello-plugin'; version = '1.0.0';
install(app: VeloceTS) { // Add a route app.get('/hello', { handler: async (c) => { return { message: 'Hello from plugin!' }; }, });
console.log('HelloPlugin installed'); }}
// Use the pluginconst app = new Veloce();app.usePlugin(new HelloPlugin());Plugin with Configuration
Section titled “Plugin with Configuration”interface LoggerPluginConfig { level: 'debug' | 'info' | 'warn' | 'error'; format: 'json' | 'text';}
class LoggerPlugin implements Plugin { name = 'logger-plugin'; version = '1.0.0';
constructor(private config: LoggerPluginConfig) {}
install(app: VeloceTS) { // Add logging middleware app.use(async (c, next) => { const start = Date.now();
await next();
const duration = Date.now() - start; const log = { method: c.req.method, url: c.req.url, status: c.res.status, duration: `${duration}ms`, };
if (this.config.format === 'json') { console.log(JSON.stringify(log)); } else { console.log(`${log.method} ${log.url} - ${log.status} (${log.duration})`); } }); }}
// Use with configurationapp.usePlugin(new LoggerPlugin({ level: 'info', format: 'json',}));Async Plugin
Section titled “Async Plugin”class DatabasePlugin implements Plugin { name = 'database-plugin';
async install(app: VeloceTS) { // Async initialization const db = await this.connectDatabase();
// Register as singleton app.getContainer().register(DatabaseService, { scope: 'singleton', factory: () => db, });
console.log('Database connected'); }
private async connectDatabase() { // Connect to database return new DatabaseService(); }}Plugin with Dependencies
Section titled “Plugin with Dependencies”class AnalyticsPlugin implements Plugin { name = 'analytics-plugin'; version = '1.0.0'; dependencies = ['logger-plugin'];
install(app: VeloceTS) { // This plugin requires logger-plugin to be installed first app.use(async (c, next) => { // Track request await next(); // Send analytics }); }}Plugin Examples
Section titled “Plugin Examples”Authentication Plugin
Section titled “Authentication Plugin”interface AuthPluginConfig { secret: string; expiresIn: string;}
class AuthPlugin implements Plugin { name = 'auth-plugin';
constructor(private config: AuthPluginConfig) {}
install(app: VeloceTS) { // Add auth service class AuthService { generateToken(userId: string) { // Generate JWT token return 'token'; }
verifyToken(token: string) { // Verify JWT token return { userId: '1' }; } }
app.getContainer().register(AuthService, { scope: 'singleton' });
// Add auth routes app.post('/auth/login', { handler: async (c) => { const { email, password } = await c.req.json(); // Validate credentials const token = 'generated-token'; return { token }; }, });
app.post('/auth/logout', { handler: async (c) => { return { success: true }; }, }); }}Rate Limiting Plugin
Section titled “Rate Limiting Plugin”interface RateLimitConfig { windowMs: number; max: number;}
class RateLimitPlugin implements Plugin { name = 'rate-limit-plugin'; private requests = new Map<string, number[]>();
constructor(private config: RateLimitConfig) {}
install(app: VeloceTS) { app.use(async (c, next) => { const ip = c.req.header('x-forwarded-for') || 'unknown'; const now = Date.now();
// Get request timestamps for this IP const timestamps = this.requests.get(ip) || [];
// Remove old timestamps const validTimestamps = timestamps.filter( t => now - t < this.config.windowMs );
// Check if limit exceeded if (validTimestamps.length >= this.config.max) { return c.json({ error: 'Too many requests' }, 429); }
// Add current timestamp validTimestamps.push(now); this.requests.set(ip, validTimestamps);
await next(); }); }}
app.usePlugin(new RateLimitPlugin({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // 100 requests per window}));Caching Plugin
Section titled “Caching Plugin”interface CachePluginConfig { ttl: number; // Time to live in seconds maxSize: number; // Maximum cache size}
class CachePlugin implements Plugin { name = 'cache-plugin'; private cache = new Map<string, { data: any; expires: number }>();
constructor(private config: CachePluginConfig) {}
install(app: VeloceTS) { class CacheService { constructor(private cache: Map<string, any>) {}
get(key: string) { const item = this.cache.get(key); if (!item) return null;
if (Date.now() > item.expires) { this.cache.delete(key); return null; }
return item.data; }
set(key: string, data: any, ttl?: number) { const expires = Date.now() + (ttl || this.config.ttl) * 1000; this.cache.set(key, { data, expires }); }
delete(key: string) { this.cache.delete(key); }
clear() { this.cache.clear(); } }
app.getContainer().register(CacheService, { scope: 'singleton', factory: () => new CacheService(this.cache), }); }}Monitoring Plugin
Section titled “Monitoring Plugin”class MonitoringPlugin implements Plugin { name = 'monitoring-plugin'; private metrics = { requests: 0, errors: 0, totalDuration: 0, };
install(app: VeloceTS) { // Track metrics app.use(async (c, next) => { this.metrics.requests++; const start = Date.now();
try { await next(); } catch (error) { this.metrics.errors++; throw error; } finally { this.metrics.totalDuration += Date.now() - start; } });
// Expose metrics endpoint app.get('/metrics', { handler: async (c) => { return { ...this.metrics, averageDuration: this.metrics.totalDuration / this.metrics.requests, errorRate: this.metrics.errors / this.metrics.requests, }; }, }); }}Best Practices
Section titled “Best Practices”1. Use Descriptive Names
Section titled “1. Use Descriptive Names”// ✓ Goodclass AuthenticationPlugin implements Plugin { name = 'authentication-plugin';}
// ✗ Badclass MyPlugin implements Plugin { name = 'plugin1';}2. Version Your Plugins
Section titled “2. Version Your Plugins”class MyPlugin implements Plugin { name = 'my-plugin'; version = '1.0.0';}3. Document Configuration Options
Section titled “3. Document Configuration Options”/** * Configuration options for CachePlugin */interface CachePluginConfig { /** Time to live in seconds */ ttl: number; /** Maximum number of items in cache */ maxSize: number; /** Cache key prefix */ prefix?: string;}
class CachePlugin implements Plugin { constructor(private config: CachePluginConfig) {}}4. Handle Errors Gracefully
Section titled “4. Handle Errors Gracefully”class MyPlugin implements Plugin { name = 'my-plugin';
async install(app: VeloceTS) { try { await this.initialize(); } catch (error) { console.error(`Failed to initialize ${this.name}:`, error); throw error; } }
private async initialize() { // Initialization logic }}5. Clean Up Resources
Section titled “5. Clean Up Resources”class DatabasePlugin implements Plugin { name = 'database-plugin'; private connection: any;
async install(app: VeloceTS) { this.connection = await this.connect();
// Register cleanup handler process.on('SIGTERM', () => this.cleanup()); process.on('SIGINT', () => this.cleanup()); }
private async cleanup() { if (this.connection) { await this.connection.close(); } }}6. Make Plugins Configurable
Section titled “6. Make Plugins Configurable”// ✓ Good - Configurableclass LoggerPlugin implements Plugin { constructor(private config: LoggerConfig) {}}
// ✗ Bad - Hardcodedclass LoggerPlugin implements Plugin { private level = 'info'; // Hardcoded}7. Test Your Plugins
Section titled “7. Test Your Plugins”import { describe, it, expect } from 'bun:test';import { createTestApp } from 'veloce/testing';
describe('MyPlugin', () => { it('should add route', async () => { const app = createTestApp(); app.usePlugin(new MyPlugin());
const response = await app.request('/plugin-route'); expect(response.status).toBe(200); });});Plugin Distribution
Section titled “Plugin Distribution”Publishing to NPM
Section titled “Publishing to NPM”{ "name": "veloce-plugin-myfeature", "version": "1.0.0", "main": "dist/index.js", "types": "dist/index.d.ts", "peerDependencies": { "veloce-ts": "^0.1.0" }}npm install veloce-plugin-myfeatureimport { Veloce } from 'veloce-ts';import { MyFeaturePlugin } from 'veloce-plugin-myfeature';
const app = new Veloce();app.usePlugin(new MyFeaturePlugin());Next Steps
Section titled “Next Steps”- Read the Testing Guide
- Check out the Getting Started Guide
- Learn about Dependency Injection