| title | Metadata Service |
|---|---|
| description | The unified engine for loading, saving, and managing configuration metadata across the platform |
import { Database, FileCode, Layers, RefreshCw, Save } from 'lucide-react';
The Metadata Service is the backbone of ObjectOS configuration management. It decouples the definition of metadata (Objects, Views, Flows) from their storage (Filesystem, Database, Git), allowing the platform to run seamlessly in local dev environments, serverless functions, or distributed clusters.
The Metadata Service uses a Provider/Loader architecture. The core MetadataManager acts as a registry and orchestrator, delegating actual I/O operations to registered Loaders.
The MetadataManager is the single entry point for all metadata operations. It handles caching, serialization, and correct routing of requests to loaders.
import { MetadataManager } from '@objectstack/metadata';
const manager = new MetadataManager({
rootDir: './src',
watch: true // Enable hot reload
});
// Load an Object Definition
const accountObj = await manager.load('object', 'account');Loaders adapt different storage backends to a common interface.
| Loader | Type | Description |
|---|---|---|
| FilesystemLoader | filesystem |
Reads/Writes .json, .yaml, .ts files from disk. Primary for development. |
| DatabaseLoader | database |
Stores metadata in _framework_metadata tables. Primary for user customization. |
| RemoteLoader | http |
Fetches metadata from a remote API or git repository. |
Each Loader declares its capabilities via a Contract:
export interface MetadataLoaderContract {
name: string;
protocol: 'file' | 'http' | 'database';
capabilities: {
read: boolean; // Can read items?
write: boolean; // Can save modifications?
watch: boolean; // Can push real-time updates?
list: boolean; // Can enumerate items?
};
}The Metadata Service supports full CRUD operations. The save API is intelligent and can be directed to specific loaders.
// 1. Simple Save (Auto-detects loader)
await manager.save('object', 'customer_profile', data);
// 2. Targeted Save (Specific Source)
await manager.save('object', 'customer_profile', data, {
loader: 'filesystem',
format: 'yaml', // Convert to YAML on save
atomic: true // Use write-rename strategy for safety
});
// 3. Create Backup
await manager.save('object', 'workflow_config', data, {
backup: true // Creates 'workflow_config.json.bak' before writing
});When load() is called, the Manager iterates through registered loaders. This allows for clear precedence rules (e.g., Database overrides Filesystem).
graph LR
A[Request: load('object', 'user')] --> B{Metadata Manager}
B --> C[Check Database Loader]
C -- Not Found --> D[Check Filesystem Loader]
D -- Found --> E[Return & Cache]
C -- Found --> E
The Service emits events when metadata changes, allowing the runtime to reconfigure itself without restarting.
manager.watch('object', (event) => {
console.log(`Object ${event.name} changed!`);
console.log(`New Schema:`, event.data);
// Trigger system update
SchemaEngine.refresh(event.name);
});The system provides standard REST and TypeScript APIs for interacting with metadata, including full read/write support.
GET /api/metadata: List all metadata types.GET /api/metadata/:type: List items of a type.GET /api/metadata/:type/:name: Get simplified definition of an item.PUT /api/metadata/:type/:name: Save or update a metadata item.
// Save metadata from the browser
const result = await client.meta.saveItem('object', 'customer', {
label: 'Customer',
fields: {
name: { type: 'text', required: true }
}
});// Access from Protocol Service (Server-side)
const protocol = kernel.getService('protocol');
await protocol.saveMetaItem({
type: 'object',
name: 'lead',
item: { ... }
});The Metadata Service is designed to power the CLI.
objectstack pull: Uses thesaveAPI to write database metadata to local files.objectstack push: Uses theloadAPI to read local files and push to the database.