Extensions allow you to add custom functionality to NocoDB bases. Each extension can store its own configuration and data, enabling powerful customizations tailored to your specific needs.
Overview
Extensions are base-specific add-ons that can:
- Store custom configuration and settings
- Maintain their own key-value data store
- Integrate with external services
- Provide custom UI components
- Extend NocoDB’s core functionality
Extension Structure
Core Properties
Unique identifier for the extension instance
The identifier of the extension type being installed
Display name for the extension
ID of the base where the extension is installed
ID of the user who created the extension
Display order for the extension (auto-incremented if not provided)
Data Storage
Key-value store for extension data. Can store any JSON-serializable data.{
"settings": {
"theme": "dark",
"autoRefresh": true
},
"cache": {
"lastSync": "2026-03-03T10:30:00Z"
}
}
Metadata for the extension. Used for configuration and settings.{
"version": "1.0.0",
"author": "Your Name",
"description": "Custom analytics dashboard"
}
Creating Extensions
Basic Extension
const extension = await Extension.insert(context, {
base_id: 'base_abc123',
extension_id: 'custom-analytics',
title: 'Analytics Dashboard',
fk_user_id: 'usr_xyz789',
meta: {
version: '1.0.0',
description: 'Custom analytics for sales data'
},
kv_store: {
settings: {
refreshInterval: 60000,
defaultView: 'charts'
}
}
});
Extension with Configuration
const extension = await Extension.insert(context, {
base_id: 'base_abc123',
extension_id: 'data-sync',
title: 'External Data Sync',
meta: {
version: '2.1.0',
config: {
apiEndpoint: 'https://api.example.com',
syncInterval: 3600000, // 1 hour
fields: ['name', 'email', 'status']
}
},
kv_store: {
lastSync: null,
syncCount: 0,
errors: []
}
});
Managing Extensions
List Extensions
Get all extensions for a base:
const extensions = await Extension.list(context, baseId);
// Extensions are sorted by order
for (const ext of extensions) {
console.log(`${ext.title}: ${ext.extension_id}`);
}
Get Extension
Retrieve a specific extension:
const extension = await Extension.get(context, extensionId);
console.log(extension.kv_store);
Update Extension
Update extension data:
await Extension.update(context, extensionId, {
title: 'Updated Title',
kv_store: {
...extension.kv_store,
lastSync: new Date().toISOString(),
syncCount: extension.kv_store.syncCount + 1
},
meta: {
...extension.meta,
lastModified: new Date().toISOString()
}
});
Delete Extension
Remove an extension:
await Extension.delete(context, extensionId);
Delete All Base Extensions
Remove all extensions from a base:
await Extension.deleteByBaseId(context, baseId);
Extension Use Cases
1. Custom Dashboard
const dashboard = await Extension.insert(context, {
base_id: baseId,
extension_id: 'custom-dashboard',
title: 'Sales Dashboard',
meta: {
widgets: [
{ type: 'chart', title: 'Revenue', query: 'SELECT SUM(revenue) FROM sales' },
{ type: 'table', title: 'Top Products', query: 'SELECT * FROM products ORDER BY sales DESC LIMIT 10' }
]
},
kv_store: {
cachedData: {},
refreshInterval: 300000 // 5 minutes
}
});
2. Data Validation
const validator = await Extension.insert(context, {
base_id: baseId,
extension_id: 'data-validator',
title: 'Custom Validation Rules',
meta: {
rules: [
{ field: 'email', pattern: '^[^@]+@[^@]+\\.[^@]+$' },
{ field: 'phone', pattern: '^\\+?[0-9]{10,15}$' }
]
},
kv_store: {
validationResults: [],
lastRun: null
}
});
3. Integration Hub
const integration = await Extension.insert(context, {
base_id: baseId,
extension_id: 'integration-hub',
title: 'CRM Integration',
meta: {
connections: [
{ service: 'Salesforce', enabled: true },
{ service: 'HubSpot', enabled: false }
]
},
kv_store: {
tokens: {}, // Store OAuth tokens
syncStatus: {},
logs: []
}
});
4. Automation Workflow
const workflow = await Extension.insert(context, {
base_id: baseId,
extension_id: 'automation-workflow',
title: 'Lead Scoring Automation',
meta: {
workflow: {
triggers: [{ event: 'after.insert', table: 'leads' }],
actions: [
{ type: 'calculate', field: 'score', formula: 'budget * 0.3 + engagement * 0.7' },
{ type: 'notify', condition: 'score > 80', channel: 'slack' }
]
}
},
kv_store: {
executionHistory: [],
statistics: {
totalRuns: 0,
successRate: 0
}
}
});
Data Persistence
Key-Value Store
The kv_store is perfect for:
- Cache data
- User preferences
- Temporary state
- Runtime statistics
- Error logs
Example:
// Initialize KV store
const extension = await Extension.insert(context, {
base_id: baseId,
extension_id: 'cache-manager',
title: 'Data Cache',
kv_store: {
cache: {},
stats: {
hits: 0,
misses: 0
}
}
});
// Update KV store
const current = await Extension.get(context, extension.id);
await Extension.update(context, extension.id, {
kv_store: {
...current.kv_store,
cache: {
...current.kv_store.cache,
[`key_${Date.now()}`]: 'value'
},
stats: {
hits: current.kv_store.stats.hits + 1,
misses: current.kv_store.stats.misses
}
}
});
The meta field is ideal for:
- Extension configuration
- Schema definitions
- Static settings
- Version information
Example:
const extension = await Extension.insert(context, {
base_id: baseId,
extension_id: 'report-generator',
title: 'Monthly Reports',
meta: {
version: '1.2.0',
reportTemplates: [
{
name: 'Sales Summary',
schedule: 'monthly',
format: 'pdf',
recipients: ['manager@example.com']
}
],
config: {
timezone: 'UTC',
dateFormat: 'YYYY-MM-DD'
}
}
});
Extension Ordering
Extensions support ordering for display purposes:
// Extension with specific order
await Extension.insert(context, {
base_id: baseId,
extension_id: 'priority-extension',
title: 'High Priority Extension',
order: 1 // Will appear first
});
// Auto-increment order (default behavior)
await Extension.insert(context, {
base_id: baseId,
extension_id: 'normal-extension',
title: 'Normal Extension'
// order is automatically set
});
Workspace Limits
Extensions are subject to workspace limits:
- Maximum extensions per workspace (configurable)
- Quota tracking via
PlanLimitTypes.LIMIT_EXTENSION_PER_WORKSPACE
- Creating an extension increments the workspace counter
- Deleting an extension decrements the counter
Best Practices
- Use Descriptive IDs: Choose clear, unique
extension_id values
- Version Your Extensions: Include version in
meta for tracking changes
- Validate Data: Check
kv_store and meta data before updates
- Handle Errors: Implement error handling for extension operations
- Document Structure: Document your
meta and kv_store schemas
- Clean Up: Delete unused extensions to free up quota
- Order Strategically: Use
order to prioritize important extensions
- Separate Concerns: Use
meta for config, kv_store for runtime data
Caching
Extensions are cached for performance:
- Individual extensions cached by ID
- Base extension lists cached
- Cache automatically invalidated on updates
- Deep cache deletion on extension removal
Extensions are base-specific. When a base is deleted, all its extensions are automatically removed.
The kv_store and meta fields are JSON-serialized. Ensure all stored data is JSON-serializable.