Filter by title

Interceptors

Interceptors allow you to run custom JavaScript code before, after, or instead of Create, Update, and Delete operations on dynamic entities.

Interceptor Types

Command Type When Executed
Create Pre Before entity creation — validation, default values
Create Post After entity creation — notifications, related data
Create Replace Instead of entity creation — must return the new entity's Id (see below)
Update Pre Before entity update — validation, authorization
Update Post After entity update — sync, notifications
Update Replace Instead of entity update — no return value needed
Delete Pre Before entity deletion — dependency checks
Delete Post After entity deletion — cleanup
Delete Replace Instead of entity deletion — no return value needed

Defining Interceptors with Attributes

Use the [DynamicEntityCommandInterceptor] attribute on a C# class:

[DynamicEntity]
[DynamicEntityCommandInterceptor(
    "Create",
    InterceptorType.Pre,
    "if(!context.commandArgs.data['Name']) { globalError = 'Name is required!'; }"
)]
[DynamicEntityCommandInterceptor(
    "Create",
    InterceptorType.Post,
    "context.log('Entity created: ' + context.commandArgs.entityId);"
)]
public class Organization
{
    public string Name { get; set; }
}

The Name parameter must be one of: "Create", "Update", or "Delete". The InterceptorType can be Pre, Post, or Replace. When Replace is used, the default database operation is completely skipped and only your JavaScript handler executes. Multiple interceptors can be added to the same class (AllowMultiple = true).

Defining Interceptors with Fluent API

Use the Interceptors list on an EntityDescriptor to add interceptors programmatically in your Low-Code Initializer:

AbpDynamicEntityConfig.EntityConfigurations.Configure(
    "MyApp.Organizations.Organization",
    entity =>
    {
        entity.Interceptors.Add(new CommandInterceptorDescriptor("Create")
        {
            Type = InterceptorType.Pre,
            Javascript = "if(!context.commandArgs.data['Name']) { globalError = 'Name is required!'; }"
        });

        entity.Interceptors.Add(new CommandInterceptorDescriptor("Delete")
        {
            Type = InterceptorType.Post,
            Javascript = "context.log('Deleted: ' + context.commandArgs.entityId);"
        });
    }
);

See Attributes & Fluent API for more details on Fluent API configuration.

Defining Interceptors in model.json

Add interceptors to the interceptors array of an entity:

{
  "name": "LowCodeDemo.Customers.Customer",
  "interceptors": [
    {
      "commandName": "Create",
      "type": "Pre",
      "javascript": "if(context.commandArgs.data['Name'] == 'Invalid') {\n  globalError = 'Invalid Customer Name!';\n}"
    }
  ]
}

Interceptor Descriptor

Field Type Description
commandName string "Create", "Update", or "Delete"
type string "Pre", "Post", or "Replace"
javascript string JavaScript code to execute

JavaScript Context

Inside interceptor scripts, you have access to:

context.commandArgs

Property / Method Type Description
data object Entity data dictionary (for Create/Update)
entityId string Entity ID (for Update/Delete)
commandName string Command name ("Create", "Update", or "Delete")
entityName string Full entity name
getValue(name) function Get a property value
setValue(name, value) function Set a property value (Pre-interceptors only)
hasValue(name) function Check if a property exists in the data
removeValue(name) function Remove a property from the data

context.currentUser

Property / Method Type Description
isAuthenticated bool Whether user is logged in
id string User ID
userName string Username
email string Email address
name string First name
surName string Last name
phoneNumber string Phone number
phoneNumberVerified bool Whether phone is verified
emailVerified bool Whether email is verified
tenantId string Tenant ID (for multi-tenant apps)
roles string[] User's role names
isInRole(roleName) function Check if user has a specific role

context.emailSender

Property / Method Description
isAvailable Whether the email sender is configured and available
sendAsync(to, subject, body) Send a plain-text email
sendHtmlAsync(to, subject, htmlBody) Send an HTML email

Logging

Method Description
context.log(message) Log an informational message
context.logWarning(message) Log a warning message
context.logError(message) Log an error message

Use these methods instead of console.log (which is blocked in the sandbox).

db (Database API)

Full access to the Scripting API for querying and mutating data.

globalError

Set this variable to a string to abort the operation and return an error:

globalError = 'Cannot delete this entity!';

Interceptor validation error displayed in the UI

Examples

Pre-Create: Validation

{
  "commandName": "Create",
  "type": "Pre",
  "javascript": "if(!context.commandArgs.data['Name']) {\n  globalError = 'Organization name is required!';\n}"
}

Post-Create: Email Notification

{
  "commandName": "Create",
  "type": "Post",
  "javascript": "if(context.currentUser.isAuthenticated && context.emailSender) {\n  await context.emailSender.sendAsync(\n    context.currentUser.email,\n    'New Order Created',\n    'Order total: $' + context.commandArgs.data['TotalAmount']\n  );\n}"
}

Pre-Update: Role-Based Authorization

{
  "commandName": "Update",
  "type": "Pre",
  "javascript": "if(context.commandArgs.data['IsDelivered']) {\n  if(!context.currentUser.roles.includes('admin')) {\n    globalError = 'Only administrators can mark orders as delivered!';\n  }\n}"
}

Pre-Delete: Business Rule Check

{
  "commandName": "Delete",
  "type": "Pre",
  "javascript": "var project = await db.get('LowCodeDemo.Projects.Project', context.commandArgs.entityId);\nif(project.Budget > 100000) {\n  globalError = 'Cannot delete high-budget projects!';\n}"
}

Pre-Update: Negative Value Check

{
  "commandName": "Update",
  "type": "Pre",
  "javascript": "if(context.commandArgs.data['Quantity'] < 0) {\n  globalError = 'Quantity cannot be negative!';\n}"
}

Replace-Create: Custom Insert Logic

When you need to completely replace the default create operation with custom logic:

{
  "commandName": "Create",
  "type": "Replace",
  "javascript": "var data = context.commandArgs.data;\ndata['Code'] = 'PRD-' + Date.now();\nvar result = await db.insert('LowCodeDemo.Products.Product', data);\ncontext.log('Product created with custom code: ' + data['Code']);\nreturn result.Id;"
}

Important: Replace-Create interceptors must return the new entity's Id (Guid). The system uses this value to fetch and return the created entity. Use return result.Id; after db.insert(...).

Replace-Update and Replace-Delete interceptors do not need to return a value.

Pre-Update: Self-Reference Check

{
  "commandName": "Update",
  "type": "Pre",
  "javascript": "if(context.commandArgs.data.ParentCategoryId === context.commandArgs.entityId) {\n  globalError = 'A category cannot be its own parent!';\n}"
}

See Also

Contributors


Last updated: February 24, 2026 Edit this page on GitHub

Was this page helpful?

Please make a selection.

To help us improve, please share your reason for the negative feedback in the field below.

Please enter a note.

Thank you for your valuable feedback!

Please note that although we cannot respond to feedback, our team will use your comments to improve the experience.

1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.