Creating a Custom Datasource
This guide walks through adding support for a new datasource type (e.g., a new database or API).
Overview
Adding a datasource requires changes to three packages:
datasource-types: Define the configuration schemadatasources-logic: Implement the connection driverdatasources-ui: Create the connection form (optional, uses JSON Forms)
Step 1: Define the Type
In packages/datasource-types/src/index.js, add your new type:
export const DATASOURCE_TYPES = {
// ... existing types
CLICKHOUSE: {
name: 'ClickHouse',
value: 'clickhouse',
icon: 'ClickHouseIcon',
category: 'database'
}
};
Step 2: Create the Driver
In packages/datasources-logic/src/, create a new driver file:
// packages/datasources-logic/src/clickhouse/index.js
export class ClickHouseDriver {
constructor(config) {
this.config = config;
}
async testConnection() {
// Validate connection and return { success: true } or throw error
const client = await this.getClient();
await client.query('SELECT 1');
return { success: true };
}
async runQuery(query, params = {}) {
const client = await this.getClient();
const result = await client.query(query, params);
return {
rows: result.data,
fields: result.columns
};
}
async getTables() {
const result = await this.runQuery('SHOW TABLES');
return result.rows.map(row => ({ name: row.name }));
}
async getClient() {
// Initialize and return the ClickHouse client
const { ClickHouse } = require('@clickhouse/client');
return new ClickHouse({
host: this.config.host,
port: this.config.port,
username: this.config.username,
password: this.config.password,
database: this.config.database
});
}
}
Register the Driver
In packages/datasources-logic/src/index.js:
import { ClickHouseDriver } from './clickhouse';
export const DRIVERS = {
// ... existing drivers
clickhouse: ClickHouseDriver
};
export function getDriver(type, config) {
const Driver = DRIVERS[type];
if (!Driver) throw new Error(`Unknown datasource type: ${type}`);
return new Driver(config);
}
Step 3: Define Configuration Schema
In packages/datasource-types/src/schemas/clickhouse.js:
export const clickhouseSchema = {
type: 'object',
required: ['host', 'port', 'database'],
properties: {
host: {
type: 'string',
title: 'Host',
default: 'localhost'
},
port: {
type: 'number',
title: 'Port',
default: 8123
},
database: {
type: 'string',
title: 'Database'
},
username: {
type: 'string',
title: 'Username'
},
password: {
type: 'string',
title: 'Password',
format: 'password'
}
}
};
Step 4: Test Your Datasource
- Rebuild packages:
npm run dev:all-packages - Start the app:
npm run dev:all - Create a new datasource and select your type
- Test the connection
Driver Interface
All drivers should implement this interface:
interface DatasourceDriver {
testConnection(): Promise<{ success: boolean }>;
runQuery(query: string, params?: object): Promise<{ rows: any[], fields: any[] }>;
getTables(): Promise<{ name: string }[]>;
getColumns?(tableName: string): Promise<{ name: string, type: string }[]>;
}
Best Practices
- Error Handling: Wrap errors with meaningful messages
- Connection Pooling: Reuse connections where possible
- Timeouts: Implement query timeouts to prevent hanging
- Sanitization: Never interpolate user input directly into queries