Plugin System
Complete guide to the plugin system in c15t Backend, including plugin types, lifecycle hooks, context extensions, and best practices.
Deprecated Feature
The c15t Backend plugin system provides a powerful way to extend and customize the functionality of your consent management system. This guide covers everything you need to know about plugins, from basic usage to advanced features.
Overview
Plugins are modular components that can:
- Extend the context with additional data
- Modify requests and responses
- Add new routes and endpoints
- Provide custom middleware
- Hook into the request lifecycle
Plugin Types
Core Plugins
Core plugins are essential system components that provide fundamental functionality:
const corePlugin: C15TPlugin = {
id: 'core-plugin',
name: 'Core Plugin',
type: 'core',
init: () => ({
context: {
systemInfo: {
version: '1.0.0',
environment: process.env.NODE_ENV,
},
},
}),
};
Feature Plugins
Feature plugins add specific functionality to your system:
const featurePlugin: C15TPlugin = {
id: 'feature-plugin',
name: 'Feature Plugin',
type: 'feature',
init: () => ({
routes: [
{
path: '/api/custom',
method: 'GET',
handler: async (request, ctx) => {
return new Response(JSON.stringify({ message: 'Custom endpoint' }));
},
},
],
}),
};
Middleware Plugins
Middleware plugins process requests and responses:
const middlewarePlugin: C15TPlugin = {
id: 'middleware-plugin',
name: 'Middleware Plugin',
type: 'middleware',
onRequest: async (request, ctx) => {
// Add request processing
return { request };
},
onResponse: async (response, ctx) => {
// Add response processing
return { response };
},
};
Plugin Lifecycle
Initialization
Plugins are initialized when the c15t instance is created:
const instance = c15tInstance({
baseURL: 'http://localhost:3000',
database: memoryAdapter({}),
plugins: [
corePlugin,
featurePlugin,
middlewarePlugin,
],
});
Request Lifecycle
Plugins can hook into various stages of request processing:
const lifecyclePlugin: C15TPlugin = {
id: 'lifecycle-plugin',
name: 'Lifecycle Plugin',
type: 'middleware',
onRequest: async (request, ctx) => {
console.log('Request received');
return { request };
},
onBeforeHandler: async (request, ctx) => {
console.log('Before handler');
return { request };
},
onAfterHandler: async (response, ctx) => {
console.log('After handler');
return { response };
},
onResponse: async (response, ctx) => {
console.log('Response sent');
return { response };
},
};
Context Extensions
Plugins can extend the context with additional data:
const contextPlugin: C15TPlugin = {
id: 'context-plugin',
name: 'Context Plugin',
type: 'core',
init: () => ({
context: {
customData: {
timestamp: Date.now(),
requestId: generateId(),
},
},
}),
};
Custom Routes
Plugins can add custom routes to your API:
const routePlugin: C15TPlugin = {
id: 'route-plugin',
name: 'Route Plugin',
type: 'feature',
init: () => ({
routes: [
{
path: '/api/custom',
method: 'GET',
handler: async (request, ctx) => {
const data = await ctx.database.find('custom', {});
return new Response(JSON.stringify(data));
},
},
{
path: '/api/custom',
method: 'POST',
handler: async (request, ctx) => {
const data = await request.json();
const result = await ctx.database.create('custom', data);
return new Response(JSON.stringify(result));
},
},
],
}),
};
Error Handling
Plugins can handle errors at different levels:
const errorPlugin: C15TPlugin = {
id: 'error-plugin',
name: 'Error Plugin',
type: 'middleware',
onError: async (error, ctx) => {
console.error('Plugin error:', error);
return {
response: new Response(
JSON.stringify({ error: 'Internal server error' }),
{ status: 500 }
),
};
},
};
Plugin Dependencies
Plugins can declare dependencies on other plugins:
const dependentPlugin: C15TPlugin = {
id: 'dependent-plugin',
name: 'Dependent Plugin',
type: 'feature',
dependencies: ['core-plugin', 'auth-plugin'],
init: (ctx) => {
// Access dependent plugin data
const coreData = ctx.plugins['core-plugin'].data;
const authData = ctx.plugins['auth-plugin'].data;
return {
context: {
combinedData: {
...coreData,
...authData,
},
},
};
},
};
Best Practices
-
Plugin Organization
// plugins/index.ts export const plugins: C15TPlugin[] = [ corePlugin, authPlugin, loggingPlugin, customPlugin, ];
-
Error Handling
const safePlugin: C15TPlugin = { id: 'safe-plugin', name: 'Safe Plugin', type: 'middleware', onRequest: async (request, ctx) => { try { // Plugin logic return { request }; } catch (error) { console.error('Plugin error:', error); return { error }; } }, };
-
Performance Optimization
const optimizedPlugin: C15TPlugin = { id: 'optimized-plugin', name: 'Optimized Plugin', type: 'middleware', onRequest: async (request, ctx) => { // Cache expensive operations const cacheKey = request.url; const cached = await ctx.cache.get(cacheKey); if (cached) { return { request, cached }; } // Perform operation const result = await expensiveOperation(); await ctx.cache.set(cacheKey, result); return { request, result }; }, };
Testing Plugins
Unit Testing
describe('Custom Plugin', () => {
it('should extend context', async () => {
const plugin = customPlugin;
const ctx = { context: {} };
const result = await plugin.init(ctx);
expect(result.context).toHaveProperty('customData');
});
it('should handle requests', async () => {
const plugin = customPlugin;
const request = new Request('http://localhost:3000');
const ctx = { context: {} };
const result = await plugin.onRequest(request, ctx);
expect(result.request).toBeDefined();
});
});
Integration Testing
describe('Plugin Integration', () => {
it('should work with other plugins', async () => {
const instance = c15tInstance({
baseURL: 'http://localhost:3000',
database: memoryAdapter({}),
plugins: [plugin1, plugin2],
});
const request = new Request('http://localhost:3000/api/test');
const response = await instance.handler(request);
expect(response.status).toBe(200);
});
});
Common Issues
-
Plugin Order
- Plugins are executed in the order they are provided
- Dependencies should be listed first
- Core plugins should be initialized before feature plugins
-
Context Access
- Always check for context existence
- Use optional chaining for nested properties
- Provide default values when needed
-
Error Propagation
- Handle errors at the appropriate level
- Log errors for debugging
- Return appropriate error responses