Plugin Anatomy
The Plugin contract — interface, context, and lifecycle phases every ObjectStack plugin implements
Plugin Anatomy
Plugins are the building blocks of ObjectStack. A plugin is a plain JavaScript/TypeScript object (or class) that conforms to the Plugin interface.
Anatomy of a Plugin
import type { Plugin, PluginContext } from '@objectstack/core';
import { z } from 'zod';
export class MyPlugin implements Plugin {
// Identity
name = 'com.example.myplugin';
version = '1.0.0';
// Dependencies (Optional)
// An array of *plugin names* the kernel must initialize before this one.
// This controls init ordering — it is NOT an npm-style version map.
dependencies = ['com.objectstack.engine.objectql'];
// Configuration Schema (Optional)
// Read by the plugin loader to validate config; it lives on the plugin
// metadata rather than the base `Plugin` interface.
configSchema = z.object({
apiKey: z.string()
});
/**
* Init Phase (REQUIRED)
* Use this to register services, listeners, or other early setup.
*/
async init(ctx: PluginContext) {
// Register a service
ctx.registerService('myService', new MyService());
}
/**
* Start Phase (Optional)
* All plugins are initialized. Use this to start servers or background jobs.
*/
async start(ctx: PluginContext) {
console.log('MyPlugin started!');
}
/**
* Destroy Phase (Optional)
* Called during kernel shutdown — clean up resources here.
*/
async destroy() {
console.log('MyPlugin stopped!');
}
}Plugin Types
ObjectStack uses type discrimination to optimize runtime behavior, allowing the kernel to intelligently handle different kinds of extensions.
1. Standard Plugin (standard)
- Role: General purpose backend logic (default).
- Use Cases: Registering Services, Hooks, Utilities, or non-UI middleware.
- Behavior: Executed in the Node.js runtime with full access to the Kernel.
2. UI Plugin (ui)
- Role: Frontend application host.
- Use Cases: Admin Console, Low-code Studio, SPA Dashboard.
- Behavior:
- Passive: Driven by
plugin-hono-server(or other HTTP adapters). - Static Assets: Must provide
staticPathpointing to a build output (e.g.,dist/). - Routing: Automatically mounted to
/{slug}with SPA fallback support. - Assets: Files are served locally under
/{slug}/assets.
- Passive: Driven by
3. App Plugin (app)
- Role: Vertical Business Solution.
- Use Cases: CRM, Project Management, ERP modules.
- Behavior:
- Contains rich metadata (Objects, Flows, Reports).
- May bundle
ui-pluginreferences for custom widgets. - Focuses on business logic rather than infrastructure.
4. Driver Plugin (driver)
- Role: Infrastructure Connectivity.
- Use Cases: SQL Adaptors (
@objectstack/driver-sql), Storage (driver-s3). - Behavior: Loaded early in the lifecycle to ensure data availability.
5. Server Plugin (server)
- Role: Protocol Gateway.
- Use Cases: REST API (
plugin-hono-server), GraphQL, WebSocket. - Behavior: Responsible for binding ports and mapping incoming traffic.
6. Theme Plugin (theme)
- Role: UI Appearance.
- Use Cases: Dark Mode, Enterprise Branding.
- Behavior: Provides CSS tokens and asset overrides to
ui-plugins.
7. Agent Plugin (agent)
- Role: AI Capability.
- Use Cases: RAG Pipelines, Autonomous Agents.
- Behavior: Extensions for the AI Gateway.
Plugin Interface Reference
The Plugin interface (exported from @objectstack/core) defines the contract that all functionality modules must implement.
export interface Plugin {
/**
* Plugin Unique Identifier
* Recommended format: com.organization.plugin-name
*/
name: string;
/**
* Plugin Version (Optional)
*/
version?: string;
/**
* Plugin Type (Optional)
* One of: standard, ui, driver, server, app, theme, agent.
* @default 'standard'
*/
type?: string;
/**
* Dependencies (Optional)
* List of other plugin names that this plugin depends on.
* The kernel ensures these plugins are initialized before this one.
*/
dependencies?: string[];
/**
* Init Phase (REQUIRED)
* Called when the kernel is initializing. Use this to:
* - Register Services
* - Register Event Listeners
* - Extend Metadata
*/
init(ctx: PluginContext): Promise<void> | void;
/**
* Start Phase (Optional)
* Called after all plugins are initialized. Use this to:
* - Start HTTP servers
* - Connect to databases
* - Start background workers
*/
start?(ctx: PluginContext): Promise<void> | void;
/**
* Destroy Phase (Optional)
* Called during kernel shutdown. Clean up resources here.
*/
destroy?(): Promise<void> | void;
}Plugin Context
The PluginContext provides access to kernel capabilities:
interface PluginContext {
/** Register a service for other plugins to consume */
registerService(name: string, service: any): void;
/** Register a lifecycle-managed service factory (e.g. SCOPED per-project services) */
registerServiceFactory(
name: string,
factory: (ctx: PluginContext, scopeId?: string) => any,
lifecycle?: ServiceLifecycle,
dependencies?: string[],
): void;
/** Get a service registered by another plugin */
getService<T>(name: string): T;
/** Get a per-scope service instance (e.g. per-project / per-environment) */
getServiceScoped<T>(name: string, scopeId: string): Promise<T>;
/** Replace an existing service implementation */
replaceService<T>(name: string, implementation: T): void;
/** Get all registered services */
getServices(): Map<string, any>;
/** Register a hook handler */
hook(name: string, handler: (...args: any[]) => void | Promise<void>): void;
/** Trigger a hook */
trigger(name: string, ...args: any[]): Promise<void>;
/** Logger instance */
logger: Logger;
/** Access the kernel instance */
getKernel(): ObjectKernel;
}Plugin Lifecycle
Plugins follow a strict three-phase lifecycle managed by the kernel:
┌──────────────────────┐
│ kernel.use() │
│ Register plugin │
└──────────┬───────────┘
│
┌──────────▼───────────┐
│ Phase 1: init() │
│ Register services │
│ Set up hooks │
└──────────┬───────────┘
│
┌──────────▼───────────┐
│ Phase 2: start() │
│ Start servers │
│ Connect databases │
│ Execute logic │
└──────────┬───────────┘
│
┌──────────▼───────────┐
│ kernel:ready hook │
│ System operational │
└──────────┬───────────┘
│
┌──────────▼───────────┐
│ Phase 3: destroy() │
│ Cleanup resources │
│ (reverse order) │
└──────────────────────┘- init() — Called during kernel initialization. Register services that other plugins may depend on.
- start() — Called after all plugins have initialized. Start servers, connect to databases, or execute main logic.
- destroy() — Called during shutdown, in reverse order. Clean up connections, timers, and resources.
Next Steps
- Plugin System — Module overview: architecture, configuration, and built-in plugins
- Plugin Development — Step-by-step tutorial: build, test, and register a plugin