Skip to content

Plugins Guide

Learn how to use and create plugins for Veloce.

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.

interface Plugin {
name: string;
version?: string;
dependencies?: string[];
install(app: Veloce): void | Promise<void>;
}

Veloce-TS v0.2.6 includes several powerful built-in plugins:

JWT-based authentication with refresh tokens and user management.

import { VeloceTS, AuthPlugin } from 'veloce-ts';
const app = new VeloceTS();
app.install('auth', {
jwt: {
secret: 'your-secret-key',
accessTokenTTL: 3600, // 1 hour
refreshTokenTTL: 86400, // 24 hours
},
userProvider: {
findByCredentials: async (username: string, password: string) => {
// Your user lookup logic
},
findById: async (id: string) => {
// Your user lookup logic
},
hashPassword: async (password: string) => {
// Your password hashing logic
},
verifyPassword: async (password: string, hash: string) => {
// Your password verification logic
}
}
});

Role-Based Access Control with hierarchical roles and permissions.

import { VeloceTS, RBACPlugin } from 'veloce-ts';
const app = new VeloceTS();
app.install('rbac', {
roles: [
{ name: 'admin', level: 100 },
{ name: 'manager', level: 80 },
{ name: 'developer', level: 60 },
{ name: 'viewer', level: 20 }
],
permissions: [
'users.create', 'users.read', 'users.update', 'users.delete',
'tasks.create', 'tasks.read', 'tasks.update', 'tasks.delete'
]
});

Full GraphQL support with schema generation and introspection.

import { VeloceTS } from 'veloce-ts';
const app = new VeloceTS();
app.install('graphql', {
path: '/graphql',
playground: true,
introspection: true
});

Real-time communication with WebSocket support.

import { VeloceTS } from 'veloce-ts';
const app = new VeloceTS();
app.install('websocket', {
path: '/ws'
});

Generates OpenAPI documentation automatically from your routes.

import { VeloceTS } from 'veloce-ts';
const app = new VeloceTS();
app.install('openapi', {
path: '/openapi.json',
docsPath: '/docs',
title: 'My API',
version: '1.0.0',
description: 'API description',
}));

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

Options:

  • path: Path to serve OpenAPI JSON spec (default: /openapi.json)
  • docsPath: Path to serve Swagger UI (default: /docs)
  • title: API title
  • version: API version
  • description: API description

Access:

  • Swagger UI: http://localhost:3000/docs
  • OpenAPI Spec: http://localhost:3000/openapi.json

Enables GraphQL support with automatic schema generation.

import { Veloce, GraphQLPlugin } from 'veloce-ts';
const app = new Veloce();
app.usePlugin(new GraphQLPlugin({
path: '/graphql',
playground: true,
introspection: true,
}));

Options:

  • path: GraphQL endpoint path (default: /graphql)
  • playground: Enable GraphQL Playground (default: true)
  • introspection: Enable schema introspection (default: true)

Access:

  • GraphQL Endpoint: http://localhost:3000/graphql
  • GraphQL Playground: http://localhost:3000/graphql (in browser)

Enables WebSocket support (automatically included).

import { Veloce, WebSocketPlugin } from 'veloce-ts';
const app = new Veloce();
app.usePlugin(new WebSocketPlugin({
path: '/ws',
maxConnections: 1000,
}));
import { Veloce, OpenAPIPlugin } from 'veloce-ts';
const app = new Veloce();
// Use a single plugin
app.usePlugin(new OpenAPIPlugin());
// Use multiple plugins
app.usePlugin(new OpenAPIPlugin());
app.usePlugin(new GraphQLPlugin());
const app = new Veloce({
plugins: [
new OpenAPIPlugin({
path: '/api-docs.json',
docsPath: '/api-docs',
}),
new GraphQLPlugin({
path: '/gql',
playground: process.env.NODE_ENV !== 'production',
}),
],
});
import { Plugin, Veloce } from 'veloce-ts';
class HelloPlugin implements Plugin {
name = 'hello-plugin';
version = '1.0.0';
install(app: Veloce) {
// Add a route
app.get('/hello', {
handler: async (c) => {
return { message: 'Hello from plugin!' };
},
});
console.log('HelloPlugin installed');
}
}
// Use the plugin
const app = new Veloce();
app.usePlugin(new HelloPlugin());
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: Veloce) {
// 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 configuration
app.usePlugin(new LoggerPlugin({
level: 'info',
format: 'json',
}));
class DatabasePlugin implements Plugin {
name = 'database-plugin';
async install(app: Veloce) {
// 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();
}
}
class AnalyticsPlugin implements Plugin {
name = 'analytics-plugin';
version = '1.0.0';
dependencies = ['logger-plugin'];
install(app: Veloce) {
// This plugin requires logger-plugin to be installed first
app.use(async (c, next) => {
// Track request
await next();
// Send analytics
});
}
}
interface AuthPluginConfig {
secret: string;
expiresIn: string;
}
class AuthPlugin implements Plugin {
name = 'auth-plugin';
constructor(private config: AuthPluginConfig) {}
install(app: Veloce) {
// 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 };
},
});
}
}
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: Veloce) {
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
}));
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: Veloce) {
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),
});
}
}
class MonitoringPlugin implements Plugin {
name = 'monitoring-plugin';
private metrics = {
requests: 0,
errors: 0,
totalDuration: 0,
};
install(app: Veloce) {
// 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,
};
},
});
}
}
// ✓ Good
class AuthenticationPlugin implements Plugin {
name = 'authentication-plugin';
}
// ✗ Bad
class MyPlugin implements Plugin {
name = 'plugin1';
}
class MyPlugin implements Plugin {
name = 'my-plugin';
version = '1.0.0';
}
/**
* 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) {}
}
class MyPlugin implements Plugin {
name = 'my-plugin';
async install(app: Veloce) {
try {
await this.initialize();
} catch (error) {
console.error(`Failed to initialize ${this.name}:`, error);
throw error;
}
}
private async initialize() {
// Initialization logic
}
}
class DatabasePlugin implements Plugin {
name = 'database-plugin';
private connection: any;
async install(app: Veloce) {
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();
}
}
}
// ✓ Good - Configurable
class LoggerPlugin implements Plugin {
constructor(private config: LoggerConfig) {}
}
// ✗ Bad - Hardcoded
class LoggerPlugin implements Plugin {
private level = 'info'; // Hardcoded
}
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);
});
});
{
"name": "veloce-plugin-myfeature",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"peerDependencies": {
"veloce-ts": "^0.1.0"
}
}
Terminal window
npm install veloce-plugin-myfeature
import { Veloce } from 'veloce-ts';
import { MyFeaturePlugin } from 'veloce-plugin-myfeature';
const app = new Veloce();
app.usePlugin(new MyFeaturePlugin());