Extension mechanisms
ApiGo provides extension capabilities at three levels: data source drivers (integrate new databases/storage), result processing plugins (transform returned data), and runtime configuration hooks (change behavior without touching code). This document explains how extension points are loaded and executed from a code-structure perspective. For usage instructions, see Data plugins and Data source management.
Data source driver SPI
Loading
Data source drivers are discovered through the Java ServiceLoader, with no need to modify core code:
Adding a driver takes three steps:
- Implement the
DataSourceDriverinterface (or extendDefaultDataSourceDriver). - Write the fully qualified implementation class name into the module's
META-INF/services/cn.apigo.core.spi.datasource.DataSourceDriver. - Add the module to the dependencies of
apigo-apiandapigo-adminso thatServiceLoaderdiscovers it on both sides.
DataSourceManager registers drivers by getName() during initialization; a duplicate or empty name throws a 51003/51004 exception and blocks service startup. Type matching is always lowercased.
Interface responsibilities
| Method | Purpose |
|---|---|
getName() | Driver type identifier, corresponding to datasourceType in the data source configuration |
isRelational() | Whether the data source is relational; determines whether transactions and the JDBC connection pool are used |
supportsTransaction() / supportsStatement() / supportsMetaData() | Capability declarations; the execution engine chooses the execution path accordingly |
test() | Data source connectivity test |
init() / destroy() | Connection pool or client lifecycle |
getMetaData() | Database/table structure browsing (tables, fields, indexes) |
getStatement() | SQL/query statement execution |
Two driver types
- JDBC driver:
JdbcDataSourceDriverextendsDefaultDataSourceDriverand uniformly handles connection pools, transactions, metadata, andStatementMapper. Relational databases such as MySQL, Oracle, and PostgreSQL share this implementation; differences come from the JDBC URL and driver package. - Native driver: MongoDB and Elasticsearch implement
DataSourceDriverdirectly, declareisRelational=false, and manage clients and query statements themselves. Such data sources do not participate in SQL transactions, and their metadata capabilities are implemented per driver.
Connection pool isolation
DataSourceManager maintains three mappings in-process:
| Mapping | Content |
|---|---|
pluginType | Driver type → driver instance |
pluginDataSourcePool | Data source ID → native driver instance |
DATA_SOURCE_POOL_JDBC | Data source ID → JDBC DataSource |
When a data source switches driver type, resources of the old type are released to avoid connection pool leaks. Each admin instance maintains its own local connection pool; only the configuration snapshot in Redis is shared.
Result processing plugins
Execution chain
Plugins run after API execution completes and before the response is written; the input is the result data and the output is the transformed data. PluginExecutor works as follows:
- Parse the
pluginConfigJSON on the API. Its structure is{ "<pluginType>": { "enabled": true, ... } }. - Match a handler by
getType()among thePluginHandlerbeans registered in the Spring container. - Keep only plugins with
enabled=trueand execute them in ascendinggetOrder(). - The output of each handler becomes the input of the next.
Built-in handlers
| Type | Order | Responsibility |
|---|---|---|
fieldMapping | 5 | Field renaming / mapping |
dataProcess | 10 | Filtering, sorting, masking, encryption, calculation |
dictTransform | 11 | Dictionary value to text |
fieldFormat | 12 | Date, amount, percentage, case, whitespace trimming |
fieldReplace | 13 | Null / regex match replacement |
dataMerge | 14 | Merge multiple fields into one field |
dataAggregation | 15 | Group aggregation (sum, count, average, max, min) |
treeTransform | 30 | List to tree |
singleObject | 35 | Take a single record from a list |
nestedStructure | 40 | Build multi-level nesting by path |
dataAggregation is implemented and registered, but the current frontend plugin panel does not expose an entry for it. It still takes effect when configured through the API definition JSON; this is a backend capability the UI does not yet expose.
Fault tolerance semantics
When plugin execution throws an exception, PluginExecutor logs the error and returns the original data, rather than failing the API call. This design ensures that "processing logic errors" do not escalate into "API unavailable", but it also means plugin configuration errors can only be found in logs and never appear in the response code.
Adding a handler
- Implement
PluginHandler, returning a uniquegetType()and a suitablegetOrder(). isEnabled()reads theenabledfield from the configuration by default; it usually does not need to be overridden.- Register it as a Spring bean in
PluginConfiguration, or declare the bean in any@Configurationclass. - The frontend needs a corresponding configuration panel; otherwise the handler can only be used through the
pluginConfigJSON in the API definition.
Handlers should be idempotent, free of external side effects, and assume the input may be a single object or a list.
Reports and data processing boundaries
| Capability | Extension method | Requires code changes |
|---|---|---|
| Integrate a new database | Implement DataSourceDriver + SPI registration | Yes |
| Modify the returned field structure | Configure data plugins | No |
| Adapt SQL dialect differences | JDBC driver or custom StatementMapper | Depends |
| Add AI tools | Declare @Tool methods in McpAdminToolAdapter | Yes |
| Adjust flow control/cache/alert behavior | Configuration in the admin UI | No |
| Replace the License signature algorithm | Implement SignatureAlgorithm | Yes (vendor side) |
Extension boundaries with the admin service
The admin and execution services share the same apigo-core and apigo-plugin, therefore:
- Data source drivers must be loadable on both sides. If a driver is added only to the execution service dependencies, the admin's connection test and metadata browsing will fail.
- Result plugins only take effect in the execution path, but the admin also runs them when previewing an API, providing WYSIWYG behavior.
- Changing plugin execution order affects every API that has the plugin enabled—this is a global behavior change that requires regression testing.