Node.js platform utilities for ObjectQL - File system integration, YAML loading, and plugin management.
- 📂 File System Metadata Loader - Automatically discover and load
.object.yml,.validation.yml,.permission.ymlfiles from directories - 🔌 Plugin System - Dynamic loading and management of ObjectQL plugins
- 📦 Module System - Package and module discovery for organized project structures
- 🚀 Driver Registration - Simplified database driver configuration
- 🔍 Convention-Based Discovery - Automatic metadata scanning using glob patterns
- ⚡ Hot Reload Ready - File watching support for development workflows
npm install @objectql/platform-node @objectql/core @objectql/typesimport { ObjectQL } from '@objectql/core';
import { ObjectLoader } from '@objectql/platform-node';
import * as path from 'path';
// Initialize ObjectQL
const app = new ObjectQL({
datasources: {
default: new SqlDriver({ /* config */ })
}
});
// Create loader and load metadata from directory
const loader = new ObjectLoader(app.metadata);
loader.load(path.join(__dirname, 'src/objects'));
await app.init();import { ObjectLoader } from '@objectql/platform-node';
const loader = new ObjectLoader(app.metadata);
// Load from multiple directories
loader.load('./src/core/objects');
loader.load('./src/plugins/crm/objects');
loader.load('./src/plugins/project/objects');
// Load specific file types
loader.load('./src/validations', {
include: ['**/*.validation.yml']
});The main class for loading metadata files from the file system.
new ObjectLoader(registry: MetadataRegistry)Parameters:
registry- The MetadataRegistry from ObjectQL instance
Load metadata files from a directory.
loader.load('./src/objects');
// With options
loader.load('./src', {
include: ['**/*.object.yml', '**/*.validation.yml'],
exclude: ['**/node_modules/**', '**/test/**']
});Options:
include?: string[]- Glob patterns to include (default: all supported types)exclude?: string[]- Glob patterns to exclude
Register a custom loader plugin for handling additional file types.
loader.use({
name: 'custom-metadata',
glob: ['**/*.custom.yml'],
handler: (ctx) => {
const data = yaml.load(ctx.content);
// Process and register custom metadata
}
});Load external plugins dynamically.
import { loadPlugin } from '@objectql/platform-node';
const plugin = loadPlugin('@objectql/plugin-audit');
app.use(plugin);The plugin loader:
- Resolves the package from
node_modules - Supports both class-based and instance-based plugins
- Automatically instantiates classes if needed
- Searches default export and named exports
Simplified driver registration for Node.js environments.
import { registerDriver } from '@objectql/platform-node';
import { SqlDriver } from '@objectql/driver-sql';
registerDriver(app, 'default', new SqlDriver({
client: 'postgresql',
connection: {
host: 'localhost',
port: 5432,
database: 'myapp',
user: 'postgres',
password: 'password'
}
}));The loader automatically handles these file patterns:
| Pattern | Description | Status |
|---|---|---|
**/*.object.yml |
Object/Entity definitions | ✅ Fully Supported |
**/*.object.yaml |
Object definitions (YAML format) | ✅ Fully Supported |
**/*.validation.yml |
Validation rules | ✅ Fully Supported |
**/*.permission.yml |
Permission/RBAC rules | |
**/*.hook.yml |
Lifecycle hooks metadata | ✅ Fully Supported |
**/*.action.yml |
Custom action definitions | ✅ Fully Supported |
**/*.workflow.yml |
Workflow automation | |
**/*.app.yml |
Application configuration | ✅ Fully Supported |
**/*.data.yml |
Initial/seed data | ✅ Fully Supported |
Note: Permission and workflow files can be loaded, but require application-layer implementation. See Implementation Status for details.
my-app/
├── src/
│ ├── objects/
│ │ ├── user.object.yml
│ │ ├── project.object.yml
│ │ └── task.object.yml
│ ├── validations/
│ │ ├── user.validation.yml
│ │ └── project.validation.yml
│ └── permissions/
│ ├── user.permission.yml
│ └── project.permission.yml
└── objectstack.config.ts
my-app/
├── src/
│ ├── modules/
│ │ ├── crm/
│ │ │ ├── objects/
│ │ │ │ ├── customer.object.yml
│ │ │ │ └── opportunity.object.yml
│ │ │ └── validations/
│ │ │ └── customer.validation.yml
│ │ └── project/
│ │ ├── objects/
│ │ │ ├── project.object.yml
│ │ │ └── milestone.object.yml
│ │ └── permissions/
│ │ └── project.permission.yml
└── objectstack.config.ts
import { ObjectQL } from '@objectql/core';
import { SqlDriver } from '@objectql/driver-sql';
import { ObjectLoader, loadPlugin } from '@objectql/platform-node';
import * as path from 'path';
// Initialize ObjectQL
const app = new ObjectQL({
datasources: {
default: new SqlDriver({
client: 'sqlite3',
connection: {
filename: path.join(__dirname, 'dev.sqlite3')
},
useNullAsDefault: true
})
}
});
// Load metadata from file system
const loader = new ObjectLoader(app.metadata);
// Load core objects
loader.load(path.join(__dirname, 'src/objects'));
// Load module-specific metadata
loader.load(path.join(__dirname, 'src/modules/crm'));
loader.load(path.join(__dirname, 'src/modules/project'));
// Load plugins
try {
const auditPlugin = loadPlugin('@objectql/plugin-audit');
app.use(auditPlugin);
} catch (e) {
console.log('Audit plugin not installed');
}
export default app;Create custom handlers for specialized file types:
import { LoaderPlugin } from '@objectql/types';
import * as yaml from 'js-yaml';
const customPlugin: LoaderPlugin = {
name: 'report-loader',
glob: ['**/*.report.yml'],
handler: (ctx) => {
const report = yaml.load(ctx.content);
// Validate report structure
if (!report.name || !report.query) {
console.warn(`Invalid report in ${ctx.file}`);
return;
}
// Register report in metadata
ctx.registry.addEntry('report', report.name, {
...report,
_source: ctx.file
});
}
};
loader.use(customPlugin);Load different metadata based on environment:
const loader = new ObjectLoader(app.metadata);
// Always load core
loader.load('./src/objects');
// Environment-specific
if (process.env.NODE_ENV === 'development') {
loader.load('./src/dev-objects');
loader.load('./src/test-data');
} else if (process.env.NODE_ENV === 'production') {
loader.load('./src/production-objects');
}const loader = new ObjectLoader(app.metadata);
try {
loader.load('./src/objects');
} catch (error) {
console.error('Failed to load metadata:', error);
if (error.code === 'ENOENT') {
console.error('Directory not found. Creating...');
fs.mkdirSync('./src/objects', { recursive: true });
}
throw error;
}While the loader doesn't include built-in file watching, you can easily add it:
import * as chokidar from 'chokidar';
const loader = new ObjectLoader(app.metadata);
const watchPath = path.join(__dirname, 'src/objects');
// Initial load
loader.load(watchPath);
// Watch for changes
if (process.env.NODE_ENV === 'development') {
const watcher = chokidar.watch('**/*.object.yml', {
cwd: watchPath,
ignoreInitial: true
});
watcher.on('change', (filePath) => {
console.log(`Reloading ${filePath}...`);
// Clear and reload
app.metadata.clear();
loader.load(watchPath);
// Re-initialize
app.init();
});
}The platform-node package includes utilities for discovering packages and modules:
import { discoverModules } from '@objectql/platform-node';
// Discover all modules in a directory
const modules = discoverModules('./src/modules');
for (const module of modules) {
console.log(`Loading module: ${module.name}`);
loader.load(module.path);
}src/
modules/
users/
objects/
validations/
permissions/
hooks/
projects/
objects/
validations/
- Match file names to object names:
user.object.ymlfor object "user" - Use singular names:
project, notprojects - Use lowercase with underscores:
project_task, notProjectTask
Keep different metadata types in separate files:
user.object.yml- Object structureuser.validation.yml- Validation rulesuser.permission.yml- Access control
// Load base configuration
loader.load('./src/objects');
// Add environment-specific overrides
if (process.env.NODE_ENV === 'production') {
loader.load('./src/objects/production');
}Problem: Metadata files are not being loaded.
Solutions:
- Verify file extensions match:
.ymlor.yaml - Check file names follow conventions:
*.object.yml,*.validation.yml - Ensure directory path is correct (absolute or relative to
process.cwd()) - Check for YAML syntax errors in files
Problem: loadPlugin() throws "Failed to resolve plugin" error.
Solutions:
- Ensure plugin package is installed:
npm install @objectql/plugin-name - Verify package name is correct
- Check that plugin exports a valid ObjectQL plugin
- Try using absolute path if relative resolution fails
Problem: Slow metadata loading on startup.
Solutions:
- Limit glob patterns to specific directories
- Use
excludepatterns to skip unnecessary directories - Consider lazy loading modules
- Cache parsed metadata in production
Full TypeScript support with type definitions:
import {
ObjectLoader,
LoaderPlugin,
LoaderHandlerContext,
loadPlugin
} from '@objectql/platform-node';
const loader: ObjectLoader = new ObjectLoader(app.metadata);
const plugin: LoaderPlugin = {
name: 'custom',
glob: ['**/*.custom.yml'],
handler: (ctx: LoaderHandlerContext) => {
// Fully typed context
console.log(ctx.file, ctx.content, ctx.registry);
}
};
loader.use(plugin);- @objectql/core - Core ObjectQL engine
- @objectql/types - Type definitions
- @objectql/cli - Command-line interface
- Node.js: 14.x or higher
- TypeScript: 4.5 or higher (for TypeScript projects)
fast-glob- Fast file system glob matchingjs-yaml- YAML parsing@objectql/types- Core type definitions@objectql/core- Core utilities
MIT - Same as ObjectQL
Contributions are welcome! Please see the main repository README for guidelines.