ObjectStack Core Runtime & Query Engine
npm install @objectstack/runtimeObjectStack Standard System Library
The runtime package provides the Standard Library for the ObjectStack Operating System. It bridges the pure ObjectKernel (from @objectstack/core) with the Data Engine (@objectstack/objectql) and provides essential infrastructure adapters.
- Standard Library: Contains essential plugins (AppPlugin, DriverPlugin)
- Core Integration: Re-exports ObjectKernel for convenience
- Capability Contracts: Abstract interfaces for HTTP server and data persistence
Role: Server Runtime & REST API
Usage:
- Use RestServer to spawn HTTP endpoints.
- Configures the HTTP layer.
Key Concepts:
- RestServer: Generates /api/* endpoints from ObjectQL schemas.
``bash`
npm install @objectstack/runtime
`typescript
import { ObjectKernel } from '@objectstack/core';
import { DriverPlugin, AppPlugin } from '@objectstack/runtime';
import { ObjectQLPlugin } from '@objectstack/objectql';
import { InMemoryDriver } from '@objectstack/driver-memory';
const kernel = new ObjectKernel();
kernel
// Register ObjectQL engine
.use(new ObjectQLPlugin())
// Add database driver
.use(new DriverPlugin(new InMemoryDriver(), 'memory'))
// Add your app configurations
// .use(new AppPlugin(appConfig));
await kernel.bootstrap();
`
If you have a separate ObjectQL implementation or need custom configuration:
`typescript
import { ObjectKernel, DriverPlugin } from '@objectstack/runtime';
import { ObjectQLPlugin, ObjectQL } from '@objectstack/objectql';
// Create custom ObjectQL instance
const customQL = new ObjectQL({
env: 'production',
customConfig: true
});
// Pre-configure with custom hooks
customQL.registerHook('beforeInsert', async (ctx) => {
console.log(Inserting into ${ctx.object});
});
const kernel = new ObjectKernel();
kernel
// Use your custom ObjectQL instance
.use(new ObjectQLPlugin(customQL))
// Add driver
.use(new DriverPlugin(new InMemoryDriver(), 'memory'));
await kernel.bootstrap();
// Access ObjectQL via service registry
const objectql = kernel.getService('objectql');
`
The kernel provides:
- Plugin Lifecycle Management: init → start → destroy phases
- Service Registry: Dependency injection container
- Event/Hook System: Inter-plugin communication
- Dependency Resolution: Topological sort for plugin dependencies
#### ObjectQLPlugin
Registers the ObjectQL data engine as a service.
`typescript`
new ObjectQLPlugin() // Default instance
new ObjectQLPlugin(customQL) // Custom instance
new ObjectQLPlugin(undefined, { env: 'prod' }) // With context
Services: 'objectql'
#### DriverPlugin
Registers a data driver with ObjectQL.
`typescript`
new DriverPlugin(driver, 'driver-name')
Dependencies: ['com.objectstack.engine.objectql']
#### AppPlugin
Wraps ObjectStack app manifests (objectstack.config.ts) as plugins.
`typescript`
new AppPlugin(appConfig)
Services: 'app.{id}'
#### IHttpServer
Abstract interface for HTTP server capabilities. Allows plugins to work with any HTTP framework (Express, Fastify, Hono, etc.) without tight coupling.
`typescript
import { IHttpServer, IHttpRequest, IHttpResponse } from '@objectstack/runtime';
// In your HTTP server plugin
class MyHttpServerPlugin implements Plugin {
name = 'http-server';
async init(ctx: PluginContext) {
const server: IHttpServer = createMyServer(); // Express, Hono, etc.
ctx.registerService('http-server', server);
}
}
// In your API plugin
class MyApiPlugin implements Plugin {
name = 'api';
dependencies = ['http-server'];
async start(ctx: PluginContext) {
const server = ctx.getService
// Register routes - works with any HTTP framework
server.get('/api/users', async (req, res) => {
res.json({ users: [] });
});
}
}
`
Interface Methods:
- get(path, handler) - Register GET routepost(path, handler)
- - Register POST route put(path, handler)
- - Register PUT routedelete(path, handler)
- - Register DELETE routepatch(path, handler)
- - Register PATCH routeuse(path, handler?)
- - Register middlewarelisten(port)
- - Start serverclose()
- - Stop server (optional)
#### IDataEngine
Abstract interface for data persistence. Allows plugins to work with any data layer (ObjectQL, Prisma, TypeORM, etc.) without tight coupling.
`typescript
import { IDataEngine } from '@objectstack/runtime';
// In your data plugin
class MyDataPlugin implements Plugin {
name = 'data';
async init(ctx: PluginContext) {
const engine: IDataEngine = createMyDataEngine(); // ObjectQL, Prisma, etc.
ctx.registerService('data-engine', engine);
}
}
// In your business logic plugin
class MyBusinessPlugin implements Plugin {
name = 'business';
dependencies = ['data'];
async start(ctx: PluginContext) {
const engine = ctx.getService
// CRUD operations - works with any data layer
const user = await engine.insert('user', { name: 'John' });
const users = await engine.find('user', { filter: { active: true } });
await engine.update('user', user.id, { name: 'Jane' });
await engine.delete('user', user.id);
}
}
`
Interface Methods:
- insert(objectName, data) - Create a recordfind(objectName, query?)
- - Query recordsupdate(objectName, id, data)
- - Update a recorddelete(objectName, id)
- - Delete a record
#### Methods
- use(plugin: Plugin): Register a pluginbootstrap()
- : Initialize and start all pluginsshutdown()
- : Stop all plugins in reverse ordergetService
- : Get a service from registryisRunning()
- : Check if kernel is runninggetState()
- : Get current kernel state
`typescript`
interface Plugin {
name: string; // Unique identifier
version?: string; // Plugin version
dependencies?: string[]; // Required plugin names
init(ctx: PluginContext): Promise
start?(ctx: PluginContext): Promise
destroy?(): Promise
}
`typescript`
interface PluginContext {
registerService(name: string, service: any): void;
getService
hook(name: string, handler: Function): void;
trigger(name: string, ...args: any[]): Promise
logger: Console;
getKernel?(): any;
}
See the examples/ directory for complete examples:examples/host/
- - Full server setup with Honoexamples/msw-react-crud/
- - Browser-based setup with MSWtest-mini-kernel.ts
- - Comprehensive kernel test suitepackages/runtime/src/
-
1. True Modularity: Each plugin is independent and reusable
2. Capability Contracts: Plugins depend on interfaces, not implementations
3. Testability: Mock services easily in tests
4. Flexibility: Load plugins conditionally, swap implementations
5. Extensibility: Add new plugins without modifying kernel
6. Clear Dependencies: Explicit dependency declarations
7. Better Architecture: Separation of concerns with Dependency Inversion
1. Keep plugins focused: One responsibility per plugin
2. Use services: Share functionality via service registry
3. Declare dependencies: Make plugin requirements explicit
4. Use hooks: Decouple plugins with event system
5. Handle errors: Implement proper error handling in lifecycle methods
``typescript
import { Plugin, PluginContext } from '@objectstack/core';
export class DatabasePlugin implements Plugin {
name = 'database';
private connection: any;
async init(ctx: PluginContext) {
// Initialize connection
this.connection = await createConnection({
host: 'localhost',
database: 'myapp'
});
// Register as service
ctx.registerService('database', this.connection);
ctx.logger.info('Database connected');
}
async destroy() {
// Cleanup
await this.connection.close();
}
}
`
`typescript
import { Plugin, PluginContext } from '@objectstack/core';
export class RepositoryPlugin implements Plugin {
name = 'repository';
dependencies = ['database']; // Ensure database loads first
async init(ctx: PluginContext) {
// Get dependency
const db = ctx.getService('database');
// Create and register repository
const repo = new UserRepository(db);
ctx.registerService('user-repository', repo);
}
}
`
`typescript
import { Plugin, PluginContext } from '@objectstack/core';
// Publisher
export class DataPlugin implements Plugin {
name = 'data';
async init(ctx: PluginContext) {
const service = {
async create(entity: string, data: any) {
const result = await db.insert(entity, data);
// Trigger event
await ctx.trigger('data:created', { entity, data: result });
return result;
}
};
ctx.registerService('data', service);
}
}
// Subscriber
export class AuditPlugin implements Plugin {
name = 'audit';
dependencies = ['data'];
async init(ctx: PluginContext) {
// Listen to events
ctx.hook('data:created', async ({ entity, data }) => {
ctx.logger.info(Audit: ${entity} created, { id: data.id });`
await auditLog.write({
action: 'create',
entity,
entityId: data.id,
timestamp: new Date()
});
});
}
}
`typescript
import { Plugin, PluginContext } from '@objectstack/core';
import { z } from 'zod';
const ConfigSchema = z.object({
apiKey: z.string(),
endpoint: z.string().url(),
timeout: z.number().default(5000)
});
export class ApiPlugin implements Plugin {
name = 'api-client';
constructor(private config: z.infer
// Validate config
ConfigSchema.parse(config);
}
async init(ctx: PluginContext) {
const client = new ApiClient({
apiKey: this.config.apiKey,
endpoint: this.config.endpoint,
timeout: this.config.timeout
});
ctx.registerService('api-client', client);
}
}
// Usage
kernel.use(new ApiPlugin({
apiKey: process.env.API_KEY,
endpoint: 'https://api.example.com',
timeout: 10000
}));
`
`typescript
import { Plugin, PluginContext } from '@objectstack/core';
export class ConnectionPoolPlugin implements Plugin {
name = 'connection-pool';
private pool: any;
async init(ctx: PluginContext) {
this.pool = {
connections: new Map(),
getConnection(name: string) {
if (!this.connections.has(name)) {
this.connections.set(name, createConnection(name));
}
return this.connections.get(name);
},
closeAll() {
for (const conn of this.connections.values()) {
conn.close();
}
this.connections.clear();
}
};
ctx.registerService('connection-pool', this.pool);
}
async destroy() {
// Use stored reference from init phase
if (this.pool) {
this.pool.closeAll();
}
}
}
`
`typescript
import { Plugin, PluginContext } from '@objectstack/core';
export class LoggingMiddleware implements Plugin {
name = 'logging-middleware';
dependencies = ['http-server'];
async start(ctx: PluginContext) {
const server = ctx.getService('http-server');
// Register middleware
server.use(async (req, res, next) => {
const start = Date.now();
ctx.logger.info('Request', {
method: req.method,
path: req.path
});
await next();
const duration = Date.now() - start;
ctx.logger.info('Response', {
method: req.method,
path: req.path,
status: res.statusCode,
duration
});
});
}
}
`
`typescript
import { Plugin, PluginContext } from '@objectstack/core';
export class HeavyServicePlugin implements Plugin {
name = 'heavy-service';
private instance: any = null;
async init(ctx: PluginContext) {
// Register factory instead of instance
const factory = {
async getInstance() {
if (!this.instance) {
ctx.logger.info('Lazy loading heavy service...');
this.instance = await loadHeavyService();
}
return this.instance;
}
};
ctx.registerService('heavy-service', factory);
}
}
// Usage
const factory = kernel.getService('heavy-service');
const service = await factory.getInstance(); // Loaded only when needed
`
`typescript
import { Plugin, PluginContext } from '@objectstack/core';
export class HealthCheckPlugin implements Plugin {
name = 'health-check';
dependencies = ['http-server', 'database', 'cache'];
async start(ctx: PluginContext) {
const server = ctx.getService('http-server');
server.get('/health', async (req, res) => {
const checks = await Promise.all([
this.checkDatabase(ctx),
this.checkCache(ctx),
this.checkDiskSpace()
]);
const healthy = checks.every(c => c.healthy);
res.status(healthy ? 200 : 503).json({
status: healthy ? 'healthy' : 'unhealthy',
checks
});
});
}
private async checkDatabase(ctx: PluginContext) {
try {
const db = ctx.getService('database');
await db.ping();
return { name: 'database', healthy: true };
} catch (error) {
return { name: 'database', healthy: false, error: error.message };
}
}
private async checkCache(ctx: PluginContext) {
try {
const cache = ctx.getService('cache');
await cache.ping();
return { name: 'cache', healthy: true };
} catch (error) {
return { name: 'cache', healthy: false, error: error.message };
}
}
private async checkDiskSpace() {
// Implementation
return { name: 'disk', healthy: true };
}
}
``
- MiniKernel Guide - Complete API documentation and patterns
- MiniKernel Architecture - Architecture diagrams and flows
- MiniKernel Implementation - Implementation details
Apache-2.0