CRUD Engine
The CRUD Engine is the core component of the @repo/crud
package, providing enhanced CRUD operations with support for plugins, permissions, and auditing. It wraps a data provider from the @repo/data
package and adds additional functionality.
Features
- Enhanced CRUD Operations: Extends the basic CRUD operations with additional features
- Plugin System: Support for plugins that can modify behavior before and after operations
- Permissions Integration: Built-in support for access control
- Audit Logging: Automatic tracking of data changes
- Internationalization: Support for multiple languages
- Error Handling: Consistent error handling across operations
Basic Usage
Creating a CRUD Engine
import { engine } from '@repo/crud';
import { providers } from '@repo/data';
// Create a data provider
const dataProvider = providers.supabase.createProvider({
url: process.env.SUPABASE_URL,
key: process.env.SUPABASE_KEY
});
// Create a CRUD engine
const crudEngine = engine.createCrudEngine({
dataProvider,
enableAudit: true,
enablePermissions: true,
plugins: [],
defaultLocale: 'en',
supportedLocales: ['en', 'fr', 'de', 'es', 'tr']
});
Using the CRUD Engine
// Get a list of resources
const { data, total } = await crudEngine.getList({
resource: 'users',
pagination: { page: 1, perPage: 10 },
sort: { field: 'createdAt', order: 'desc' },
filter: { role: 'admin' }
});
// Get a single resource
const { data: user } = await crudEngine.getOne({
resource: 'users',
id: '123'
});
// Create a resource
const { data: newUser } = await crudEngine.create({
resource: 'users',
data: { name: 'John Doe', email: 'john@example.com' }
});
// Update a resource
const { data: updatedUser } = await crudEngine.update({
resource: 'users',
id: '123',
data: { name: 'John Smith' }
});
// Delete a resource
const { data: deletedUser } = await crudEngine.delete({
resource: 'users',
id: '123'
});
Configuration Options
The CRUD Engine accepts the following configuration options:
interface CrudEngineOptions {
/** The data provider to use */
dataProvider: CrudProvider;
/** Enable auditing of operations */
enableAudit?: boolean;
/** Audit provider for custom audit logging */
auditProvider?: AuditProvider;
/** Enable permissions checking */
enablePermissions?: boolean;
/** Permissions provider for custom permission checks */
permissionsProvider?: PermissionsProvider;
/** Plugins to use */
plugins?: CrudPlugin[];
/** Default locale for internationalization */
defaultLocale?: string;
/** Supported locales for internationalization */
supportedLocales?: string[];
/** Error handler for custom error handling */
errorHandler?: ErrorHandler;
}
Plugin System
The CRUD Engine includes a powerful plugin system that allows you to extend its functionality. Plugins can modify the behavior of CRUD operations by intercepting them before and after they are executed.
Plugin Interface
interface CrudPlugin {
/** Unique name of the plugin */
name: string;
/** Plugin initialization function */
initialize: (engine: CrudEngine) => void;
/** Hooks for different CRUD operations */
hooks?: {
beforeGetList?: (params: GetListParams) => GetListParams;
afterGetList?: (result: any, params: GetListParams) => any;
beforeGetOne?: (params: GetOneParams) => GetOneParams;
afterGetOne?: (result: any, params: GetOneParams) => any;
beforeCreate?: (params: CreateParams) => CreateParams;
afterCreate?: (result: any, params: CreateParams) => any;
beforeUpdate?: (params: UpdateParams) => UpdateParams;
afterUpdate?: (result: any, params: UpdateParams) => any;
beforeDelete?: (params: DeleteParams) => DeleteParams;
afterDelete?: (result: any, params: DeleteParams) => any;
};
}
Creating a Plugin
import { engine } from '@repo/crud';
// Create a logging plugin
const loggingPlugin: engine.CrudPlugin = {
name: 'logging',
initialize: (engine) => {
console.log('Logging plugin initialized');
},
hooks: {
beforeGetList: (params) => {
console.log('Getting list', params);
return params;
},
afterGetList: (result, params) => {
console.log('Got list', result);
return result;
},
beforeCreate: (params) => {
console.log('Creating resource', params);
return params;
},
afterCreate: (result, params) => {
console.log('Created resource', result);
return result;
}
// Add more hooks as needed
}
};
// Use the plugin
const crudEngine = engine.createCrudEngine({
dataProvider,
plugins: [loggingPlugin]
});
Plugin Execution Order
Plugins are executed in the order they are provided in the plugins
array. For example, if you have three plugins:
const crudEngine = engine.createCrudEngine({
dataProvider,
plugins: [pluginA, pluginB, pluginC]
});
The execution order for a getList
operation would be:
pluginA.hooks.beforeGetList
pluginB.hooks.beforeGetList
pluginC.hooks.beforeGetList
- Actual
getList
operation
pluginC.hooks.afterGetList
pluginB.hooks.afterGetList
pluginA.hooks.afterGetList
Note that the “after” hooks are executed in reverse order, allowing each plugin to process the result in the reverse order of the “before” hooks.
Permissions Integration
The CRUD Engine includes built-in support for permissions checking. When enabled, it will check if the current user has permission to perform the requested operation.
Enabling Permissions
import { engine, permissions } from '@repo/crud';
// Create a permissions provider
const permissionsProvider = permissions.createPermissionsProvider({
getPermissions: async (user) => {
return {
users: {
read: true,
create: user.role === 'admin',
update: (user, resource) => user.id === resource.id || user.role === 'admin',
delete: user.role === 'admin'
}
};
}
});
// Create a CRUD engine with permissions
const crudEngine = engine.createCrudEngine({
dataProvider,
enablePermissions: true,
permissionsProvider
});
Permissions Context
When using permissions, you need to set the current user in the permissions context:
import { permissions } from '@repo/crud';
// Set the current user
permissions.setUser({
id: '123',
role: 'admin'
});
// Now CRUD operations will be checked against this user
const result = await crudEngine.getList({
resource: 'users',
pagination: { page: 1, perPage: 10 }
});
Audit Logging
The CRUD Engine includes built-in support for audit logging. When enabled, it will log all changes to data.
Enabling Audit Logging
import { engine, audit } from '@repo/crud';
// Create an audit provider
const auditProvider = audit.createAuditProvider({
logAudit: async (auditLog) => {
// Store audit log in database
await db.auditLogs.create(auditLog);
}
});
// Create a CRUD engine with audit logging
const crudEngine = engine.createCrudEngine({
dataProvider,
enableAudit: true,
auditProvider
});
Audit Context
When using audit logging, you need to set the current user in the audit context:
import { audit } from '@repo/crud';
// Set the current user
audit.setUser({
id: '123',
name: 'John Doe'
});
// Now CRUD operations will include this user in audit logs
const result = await crudEngine.create({
resource: 'posts',
data: { title: 'New Post', content: 'Hello World' }
});
Internationalization
The CRUD Engine includes built-in support for internationalization. You can specify the default locale and supported locales when creating the engine.
Configuring Internationalization
import { engine } from '@repo/crud';
// Create a CRUD engine with internationalization
const crudEngine = engine.createCrudEngine({
dataProvider,
defaultLocale: 'en',
supportedLocales: ['en', 'fr', 'de', 'es', 'tr']
});
Using Translations
The CRUD Engine uses the next-intl
package for translations. You can use the useTranslation
hook to get translations:
import { useTranslation } from 'next-intl';
function MyComponent() {
const { t } = useTranslation();
return (
<div>
<h1>{t('users.title')}</h1>
<p>{t('users.description')}</p>
</div>
);
}
Error Handling
The CRUD Engine includes built-in error handling. You can provide a custom error handler when creating the engine.
Custom Error Handler
import { engine } from '@repo/crud';
// Create a custom error handler
const errorHandler: engine.ErrorHandler = (error, operation, params) => {
console.error(`Error in ${operation} operation:`, error);
console.error('Params:', params);
// You can transform the error
if (error.code === 'PERMISSION_DENIED') {
return new Error('You do not have permission to perform this operation');
}
return error;
};
// Create a CRUD engine with custom error handling
const crudEngine = engine.createCrudEngine({
dataProvider,
errorHandler
});
Advanced Usage
Creating a CRUD Engine with a Provider Type
You can create a CRUD Engine with a specific provider type:
import { engine } from '@repo/crud';
// Create a CRUD engine with a Supabase provider
const crudEngine = engine.createCrudEngineWithProvider('supabase', {
url: process.env.SUPABASE_URL,
key: process.env.SUPABASE_KEY
}, {
enableAudit: true,
enablePermissions: true
});
Extending the CRUD Engine
You can extend the CRUD Engine with custom methods:
import { engine } from '@repo/crud';
// Create a custom CRUD engine class
class CustomCrudEngine extends engine.CrudEngine {
constructor(options) {
super(options);
}
// Add custom methods
async getByEmail(resource, email) {
const { data, total } = await this.getList({
resource,
filter: { email }
});
return data[0] || null;
}
async bulkCreate(resource, items) {
const results = [];
for (const item of items) {
const result = await this.create({
resource,
data: item
});
results.push(result.data);
}
return results;
}
}
// Create a custom CRUD engine
const customEngine = new CustomCrudEngine({
dataProvider,
enableAudit: true,
enablePermissions: true
});
// Use custom methods
const user = await customEngine.getByEmail('users', 'john@example.com');
const posts = await customEngine.bulkCreate('posts', [
{ title: 'Post 1', content: 'Content 1' },
{ title: 'Post 2', content: 'Content 2' }
]);
API Reference
CrudEngine Class
class CrudEngine {
constructor(options: CrudEngineOptions);
// Get the data provider
getDataProvider(): CrudProvider;
// CRUD operations
getList(params: GetListParams): Promise<GetListResult>;
getOne(params: GetOneParams): Promise<GetOneResult>;
create(params: CreateParams): Promise<CreateResult>;
update(params: UpdateParams): Promise<UpdateResult>;
delete(params: DeleteParams): Promise<DeleteResult>;
}
Factory Functions
// Create a CRUD engine
function createCrudEngine(options: CrudEngineOptions): CrudEngine;
// Create a CRUD engine with a provider type
function createCrudEngineWithProvider(
providerType: string,
providerConfig: any,
engineOptions?: Partial<CrudEngineOptions>
): CrudEngine;
For a complete API reference, please refer to the TypeScript definitions in the package.