Custom Backend Integration
This guide covers integrating Supernal Coding with custom backend systems, including legacy systems, proprietary databases, and specialized compliance platforms. Learn how to build adapters, implement custom data sources, and create seamless integrations.
Overview
Custom backend integration enables organizations to:
- Connect legacy compliance systems
- Integrate proprietary databases
- Build custom data sources
- Create specialized compliance workflows
- Maintain existing system investments
Architecture Patterns
Adapter Pattern
interface BackendAdapter {
connect(): Promise<void>;
disconnect(): Promise<void>;
query(query: Query): Promise<Result>;
sync(data: SyncData): Promise<SyncResult>;
validate(): Promise<ValidationResult>;
}
abstract class BaseBackendAdapter implements BackendAdapter {
protected config: BackendConfig;
protected logger: Logger;
protected metrics: MetricsCollector;
constructor(config: BackendConfig) {
this.config = config;
this.logger = new Logger(config.logging);
this.metrics = new MetricsCollector(config.metrics);
}
abstract connect(): Promise<void>;
abstract disconnect(): Promise<void>;
abstract query(query: Query): Promise<Result>;
abstract sync(data: SyncData): Promise<SyncResult>;
async validate(): Promise<ValidationResult> {
try {
await this.connect();
const testQuery = this.createTestQuery();
await this.query(testQuery);
return { valid: true, issues: [] };
} catch (error) {
return {
valid: false,
issues: [{ type: 'connection', message: error.message }],
};
}
}
protected abstract createTestQuery(): Query;
}
Legacy System Adapter
class LegacyComplianceAdapter extends BaseBackendAdapter {
private connection: LegacyConnection;
private transformer: DataTransformer;
constructor(config: LegacyBackendConfig) {
super(config);
this.transformer = new DataTransformer(config.mapping);
}
async connect(): Promise<void> {
this.logger.info('Connecting to legacy system');
this.connection = new LegacyConnection({
host: this.config.host,
port: this.config.port,
database: this.config.database,
username: this.config.username,
password: this.config.password,
timeout: this.config.timeout || 30000,
});
await this.connection.connect();
this.logger.info('Connected to legacy system successfully');
}
async query(query: Query): Promise<Result> {
const startTime = Date.now();
try {
// Transform modern query to legacy format
const legacyQuery = this.transformer.transformQuery(query);
// Execute on legacy system
const legacyResult = await this.connection.execute(legacyQuery);
// Transform result back to modern format
const modernResult = this.transformer.transformResult(legacyResult);
this.metrics.recordQueryTime(Date.now() - startTime);
return modernResult;
} catch (error) {
this.logger.error('Query failed:', error);
this.metrics.recordQueryError();
throw error;
}
}
async sync(data: SyncData): Promise<SyncResult> {
const results: SyncResult = {
created: 0,
updated: 0,
failed: 0,
errors: [],
};
for (const item of data.items) {
try {
const legacyItem = this.transformer.transformToLegacy(item);
if (await this.itemExists(item.id)) {
await this.updateItem(legacyItem);
results.updated++;
} else {
await this.createItem(legacyItem);
results.created++;
}
} catch (error) {
results.failed++;
results.errors.push({
itemId: item.id,
error: error.message,
});
}
}
return results;
}
protected createTestQuery(): Query {
return {
type: 'select',
table: 'compliance_requirements',
limit: 1,
};
}
private async itemExists(id: string): Promise<boolean> {
const query = {
type: 'select',
table: 'compliance_requirements',
where: { id },
};
const result = await this.query(query);
return result.rows.length > 0;
}
private async createItem(item: LegacyItem): Promise<void> {
const query = {
type: 'insert',
table: 'compliance_requirements',
data: item,
};
await this.query(query);
}
private async updateItem(item: LegacyItem): Promise<void> {
const query = {
type: 'update',
table: 'compliance_requirements',
data: item,
where: { id: item.id },
};
await this.query(query);
}
}
Database Integration
class CustomDatabaseAdapter extends BaseBackendAdapter {
private pool: ConnectionPool;
private schema: DatabaseSchema;
constructor(config: DatabaseConfig) {
super(config);
this.schema = new DatabaseSchema(config.schema);
}
async connect(): Promise<void> {
this.pool = new ConnectionPool({
host: this.config.host,
port: this.config.port,
database: this.config.database,
user: this.config.user,
password: this.config.password,
ssl: this.config.ssl,
poolSize: this.config.poolSize || 10,
idleTimeout: this.config.idleTimeout || 30000,
});
await this.pool.connect();
await this.validateSchema();
}
async query(query: Query): Promise<Result> {
const connection = await this.pool.getConnection();
try {
const sql = this.buildSQL(query);
const result = await connection.execute(sql, query.parameters);
return {
rows: result.rows,
rowCount: result.rowCount,
fields: result.fields,
};
} finally {
this.pool.releaseConnection(connection);
}
}
async sync(data: SyncData): Promise<SyncResult> {
const connection = await this.pool.getConnection();
const transaction = await connection.beginTransaction();
try {
const results = await this.performBulkSync(connection, data);
await transaction.commit();
return results;
} catch (error) {
await transaction.rollback();
throw error;
} finally {
this.pool.releaseConnection(connection);
}
}
private async validateSchema(): Promise<void> {
const requiredTables = this.schema.getRequiredTables();
for (const table of requiredTables) {
const exists = await this.tableExists(table.name);
if (!exists) {
await this.createTable(table);
} else {
await this.validateTableSchema(table);
}
}
}
private buildSQL(query: Query): string {
switch (query.type) {
case 'select':
return this.buildSelectSQL(query);
case 'insert':
return this.buildInsertSQL(query);
case 'update':
return this.buildUpdateSQL(query);
case 'delete':
return this.buildDeleteSQL(query);
default:
throw new Error(`Unsupported query type: ${query.type}`);
}
}
protected createTestQuery(): Query {
return {
type: 'select',
table: 'requirements',
fields: ['id'],
limit: 1,
};
}
}
Data Transformation
Field Mapping
interface FieldMapping {
source: string;
target: string;
transform?: (value: any) => any;
validate?: (value: any) => boolean;
}
class DataTransformer {
private mappings: Map<string, FieldMapping[]>;
private validators: Map<string, Validator>;
constructor(config: TransformationConfig) {
this.mappings = new Map();
this.validators = new Map();
this.loadMappings(config.mappings);
this.loadValidators(config.validators);
}
transformToModern(legacyData: LegacyData): ModernData {
const mappings = this.mappings.get(legacyData.type);
if (!mappings) {
throw new Error(`No mappings found for type: ${legacyData.type}`);
}
const modernData: ModernData = {
id: legacyData.id,
type: legacyData.type,
data: {},
};
for (const mapping of mappings) {
const sourceValue = this.getNestedValue(legacyData, mapping.source);
if (sourceValue !== undefined) {
let transformedValue = sourceValue;
if (mapping.transform) {
transformedValue = mapping.transform(sourceValue);
}
if (mapping.validate && !mapping.validate(transformedValue)) {
throw new Error(`Validation failed for field: ${mapping.target}`);
}
this.setNestedValue(modernData.data, mapping.target, transformedValue);
}
}
return modernData;
}
transformToLegacy(modernData: ModernData): LegacyData {
const mappings = this.mappings.get(modernData.type);
if (!mappings) {
throw new Error(`No mappings found for type: ${modernData.type}`);
}
const legacyData: LegacyData = {
id: modernData.id,
type: modernData.type,
data: {},
};
// Reverse the mapping process
for (const mapping of mappings) {
const modernValue = this.getNestedValue(modernData.data, mapping.target);
if (modernValue !== undefined) {
let legacyValue = modernValue;
// Apply reverse transformation if available
if (mapping.reverseTransform) {
legacyValue = mapping.reverseTransform(modernValue);
}
this.setNestedValue(legacyData.data, mapping.source, legacyValue);
}
}
return legacyData;
}
private getNestedValue(obj: any, path: string): any {
return path.split('.').reduce((current, key) => current?.[key], obj);
}
private setNestedValue(obj: any, path: string, value: any): void {
const keys = path.split('.');
const lastKey = keys.pop()!;
const target = keys.reduce((current, key) => {
if (!current[key]) current[key] = {};
return current[key];
}, obj);
target[lastKey] = value;
}
}
Schema Validation
class SchemaValidator {
private schemas: Map<string, JSONSchema>;
constructor(schemas: SchemaDefinition[]) {
this.schemas = new Map();
schemas.forEach((schema) => {
this.schemas.set(schema.type, schema.definition);
});
}
validate(data: any, type: string): ValidationResult {
const schema = this.schemas.get(type);
if (!schema) {
return {
valid: false,
errors: [`No schema found for type: ${type}`],
};
}
const validator = new JSONSchemaValidator();
const result = validator.validate(data, schema);
return {
valid: result.valid,
errors: result.errors.map((error) => error.message),
};
}
addSchema(type: string, schema: JSONSchema): void {
this.schemas.set(type, schema);
}
removeSchema(type: string): void {
this.schemas.delete(type);
}
}
Real-Time Synchronization
Change Detection
class ChangeDetector {
private watchers: Map<string, ChangeWatcher> = new Map();
private eventBus: EventBus;
constructor(eventBus: EventBus) {
this.eventBus = eventBus;
}
startWatching(source: string, config: WatchConfig): void {
const watcher = new ChangeWatcher(source, config);
watcher.on('change', (change: Change) => {
this.handleChange(source, change);
});
watcher.start();
this.watchers.set(source, watcher);
}
stopWatching(source: string): void {
const watcher = this.watchers.get(source);
if (watcher) {
watcher.stop();
this.watchers.delete(source);
}
}
private async handleChange(source: string, change: Change): Promise<void> {
try {
// Validate change
const validation = await this.validateChange(change);
if (!validation.valid) {
throw new Error(`Invalid change: ${validation.errors.join(', ')}`);
}
// Transform change if needed
const transformedChange = await this.transformChange(change);
// Emit change event
this.eventBus.emit('data.changed', {
source,
change: transformedChange,
timestamp: Date.now(),
});
} catch (error) {
this.eventBus.emit('data.change_error', {
source,
change,
error: error.message,
timestamp: Date.now(),
});
}
}
private async validateChange(change: Change): Promise<ValidationResult> {
// Implement change validation logic
return { valid: true, errors: [] };
}
private async transformChange(change: Change): Promise<Change> {
// Implement change transformation logic
return change;
}
}
class ChangeWatcher extends EventEmitter {
private source: string;
private config: WatchConfig;
private polling: boolean = false;
private lastCheck: Date;
constructor(source: string, config: WatchConfig) {
super();
this.source = source;
this.config = config;
this.lastCheck = new Date();
}
start(): void {
if (this.config.method === 'polling') {
this.startPolling();
} else if (this.config.method === 'webhook') {
this.startWebhookListener();
} else if (this.config.method === 'database-trigger') {
this.startDatabaseTriggerListener();
}
}
stop(): void {
this.polling = false;
// Stop other listeners
}
private startPolling(): void {
this.polling = true;
const poll = async () => {
if (!this.polling) return;
try {
const changes = await this.detectChanges();
changes.forEach((change) => this.emit('change', change));
} catch (error) {
this.emit('error', error);
}
if (this.polling) {
setTimeout(poll, this.config.interval || 5000);
}
};
poll();
}
private async detectChanges(): Promise<Change[]> {
// Implement change detection logic based on source type
const currentCheck = new Date();
const changes: Change[] = [];
// Query for changes since last check
// This would be specific to the backend system
this.lastCheck = currentCheck;
return changes;
}
}
Conflict Resolution
class ConflictResolver {
private strategies: Map<string, ConflictStrategy>;
constructor() {
this.strategies = new Map();
this.registerDefaultStrategies();
}
async resolveConflict(conflict: DataConflict): Promise<ConflictResolution> {
const strategy = this.strategies.get(conflict.type);
if (!strategy) {
throw new Error(`No strategy found for conflict type: ${conflict.type}`);
}
return strategy.resolve(conflict);
}
registerStrategy(type: string, strategy: ConflictStrategy): void {
this.strategies.set(type, strategy);
}
private registerDefaultStrategies(): void {
// Last writer wins
this.registerStrategy('last-writer-wins', new LastWriterWinsStrategy());
// Manual resolution required
this.registerStrategy('manual', new ManualResolutionStrategy());
// Merge strategies
this.registerStrategy('merge-fields', new FieldMergeStrategy());
// Priority-based resolution
this.registerStrategy('priority-based', new PriorityBasedStrategy());
}
}
class LastWriterWinsStrategy implements ConflictStrategy {
async resolve(conflict: DataConflict): Promise<ConflictResolution> {
const latestChange = conflict.changes.reduce((latest, current) =>
current.timestamp > latest.timestamp ? current : latest
);
return {
resolution: 'automatic',
resolvedData: latestChange.data,
strategy: 'last-writer-wins',
timestamp: Date.now(),
};
}
}
class FieldMergeStrategy implements ConflictStrategy {
async resolve(conflict: DataConflict): Promise<ConflictResolution> {
const mergedData = { ...conflict.baseData };
// Merge non-conflicting fields
for (const change of conflict.changes) {
for (const [field, value] of Object.entries(change.data)) {
if (!conflict.conflictingFields.includes(field)) {
mergedData[field] = value;
}
}
}
// For conflicting fields, use latest timestamp
for (const field of conflict.conflictingFields) {
const latestChange = conflict.changes
.filter((change) => field in change.data)
.reduce((latest, current) =>
current.timestamp > latest.timestamp ? current : latest
);
mergedData[field] = latestChange.data[field];
}
return {
resolution: 'automatic',
resolvedData: mergedData,
strategy: 'field-merge',
timestamp: Date.now(),
};
}
}
Security Considerations
Authentication and Authorization
class BackendSecurityManager {
private authProviders: Map<string, AuthProvider>;
private encryptionService: EncryptionService;
private auditLogger: AuditLogger;
constructor(config: SecurityConfig) {
this.authProviders = new Map();
this.encryptionService = new EncryptionService(config.encryption);
this.auditLogger = new AuditLogger(config.audit);
this.initializeAuthProviders(config.auth);
}
async authenticateBackend(
backendId: string,
credentials: Credentials
): Promise<AuthResult> {
const provider = this.authProviders.get(backendId);
if (!provider) {
throw new Error(`No auth provider found for backend: ${backendId}`);
}
const result = await provider.authenticate(credentials);
// Log authentication attempt
await this.auditLogger.log({
action: 'backend_auth',
backendId,
success: result.success,
timestamp: Date.now(),
});
return result;
}
async encryptSensitiveData(data: any): Promise<EncryptedData> {
return this.encryptionService.encrypt(data);
}
async decryptSensitiveData(encryptedData: EncryptedData): Promise<any> {
return this.encryptionService.decrypt(encryptedData);
}
async validateAccess(
backendId: string,
operation: string,
user: User
): Promise<boolean> {
// Implement access validation logic
const permissions = await this.getBackendPermissions(backendId, user);
return permissions.includes(operation);
}
}
Data Encryption
class DataEncryptionManager {
private keyManager: KeyManager;
private encryptionAlgorithm: string;
constructor(config: EncryptionConfig) {
this.keyManager = new KeyManager(config.keyManagement);
this.encryptionAlgorithm = config.algorithm || 'AES-256-GCM';
}
async encryptField(value: any, fieldType: string): Promise<EncryptedField> {
const key = await this.keyManager.getEncryptionKey(fieldType);
const encrypted = await this.encrypt(JSON.stringify(value), key);
return {
value: encrypted.data,
iv: encrypted.iv,
tag: encrypted.tag,
algorithm: this.encryptionAlgorithm,
keyId: key.id,
};
}
async decryptField(encryptedField: EncryptedField): Promise<any> {
const key = await this.keyManager.getDecryptionKey(encryptedField.keyId);
const decrypted = await this.decrypt(encryptedField, key);
return JSON.parse(decrypted);
}
private async encrypt(data: string, key: CryptoKey): Promise<EncryptedData> {
const iv = crypto.getRandomValues(new Uint8Array(12));
const encodedData = new TextEncoder().encode(data);
const encrypted = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
key,
encodedData
);
return {
data: Array.from(new Uint8Array(encrypted)),
iv: Array.from(iv),
tag: Array.from(new Uint8Array(encrypted.slice(-16))),
};
}
}
Performance Optimization
Connection Pooling
class ConnectionPoolManager {
private pools: Map<string, ConnectionPool> = new Map();
private configs: Map<string, PoolConfig> = new Map();
createPool(backendId: string, config: PoolConfig): ConnectionPool {
const pool = new ConnectionPool({
...config,
onConnect: this.handleConnection.bind(this),
onDisconnect: this.handleDisconnection.bind(this),
onError: this.handleError.bind(this),
});
this.pools.set(backendId, pool);
this.configs.set(backendId, config);
return pool;
}
getPool(backendId: string): ConnectionPool | undefined {
return this.pools.get(backendId);
}
async closeAll(): Promise<void> {
const closePromises = Array.from(this.pools.values()).map((pool) =>
pool.close()
);
await Promise.all(closePromises);
this.pools.clear();
}
getPoolStats(): Map<string, PoolStats> {
const stats = new Map<string, PoolStats>();
for (const [backendId, pool] of this.pools) {
stats.set(backendId, {
totalConnections: pool.totalConnections,
activeConnections: pool.activeConnections,
idleConnections: pool.idleConnections,
waitingRequests: pool.waitingRequests,
});
}
return stats;
}
}
Caching Strategy
class BackendCacheManager {
private cache: Cache;
private cacheStrategies: Map<string, CacheStrategy>;
constructor(config: CacheConfig) {
this.cache = new Cache(config);
this.cacheStrategies = new Map();
this.initializeStrategies();
}
async get<T>(key: string, backendId: string): Promise<T | null> {
const strategy = this.cacheStrategies.get(backendId);
if (!strategy || !strategy.shouldCache('read')) {
return null;
}
const cached = await this.cache.get<T>(key);
if (cached && !this.isExpired(cached, strategy.ttl)) {
return cached.data;
}
return null;
}
async set<T>(key: string, value: T, backendId: string): Promise<void> {
const strategy = this.cacheStrategies.get(backendId);
if (!strategy || !strategy.shouldCache('write')) {
return;
}
await this.cache.set(key, {
data: value,
timestamp: Date.now(),
ttl: strategy.ttl,
});
}
async invalidate(pattern: string): Promise<void> {
await this.cache.invalidate(pattern);
}
}
Related Documentation
- Integration Architecture - System architecture overview
- API Reference - API integration details
- Security Guide - Security implementation
- Performance Guide - Performance optimization
Custom backend integration enables seamless connectivity with existing systems while maintaining compliance and security standards.