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