Skip to content

Customize Plugin Logic

This page explains how to customize your EMQX plugin by modifying the default template logic provided in src/my_emqx_plugin.erl. The template registers all available EMQX hooks by default. You should remove unused hooks and implement your own logic in the relevant callbacks.

Register Hook Functions

For example, to add authentication and authorization logic, register the my_emqx_plugin:hook/0 function as shown:

erlang
hook() ->
  emqx_hooks:add('client.authenticate', {?MODULE, on_client_authenticate, []}, ?HP_HIGHEST),
  emqx_hooks:add('client.authorize', {?MODULE, on_client_authorize, []}, ?HP_HIGHEST).

Here, on_client_authenticate/2 handles client authentication, while on_client_authorize/4 manages authorization.

As one hook function may be mounted by both EMQX and customized plugins, you need to specify the execution order when mounting it to the plugin. ?HP_HIGHEST specifies that the current hook function has the highest priority and is executed first.

Example: Add Access Control Logic

Here is an example implementation for basic access control:

erlang
%% Only allow connections with client IDs that match any of the characters: A-Z, a-z, 0-9, and underscore.
on_client_authenticate(_ClientInfo = #{clientid := ClientId}, Result) ->
  case re:run(ClientId, "^[A-Za-z0-9_]+$", [{capture, none}]) of
    match -> {ok, Result};
    nomatch -> {stop, {error, banned}}
  end.

%% Clients can only subscribe to topics formatted as /room/{clientid}, but can send messages to any topics.
on_client_authorize(_ClientInfo = #{clientid := ClientId}, subscribe, Topic, Result) ->
  case emqx_topic:match(Topic, <<"/room/", ClientId/binary>>) of
    true -> {ok, Result};
    false -> stop
  end;
on_client_authorize(_ClientInfo, _Pub, _Topic, Result) -> {ok, Result}.

This logic ensures:

  • Only clients with valid IDs can connect.
  • Clients can publish to any topic.
  • Clients can only subscribe to their own /room/{clientid} topic, enabling simple chat-room behavior.

TIP

Add Configuration Schema (Optional)

Starting with EMQX 5.7.0, plugin configurations can be managed dynamically via REST APIs. To enable this functionality and ensure configuration validation, your plugin should include:

  • An Avro Schema configuration file, located at the relative path priv/config_schema.avsc, for validating the configuration structure. This file must adhere to the Apache Avro specification.
  • A default configuration file at priv/config.hocon that adheres to the Avro schema rules.

At runtime, the updated configuration is saved to data/plugins/<PLUGIN_NAME>/config.hocon, and the old configuration file is backed up automatically.

TIP

Check out the example files in your project directory:

  • priv/config.hocon.example
  • priv/config_schema.avsc.example
  • priv/config_schema.avsc.enterprise.example (includes UI declarations)
  • priv/config_i18n.json.example (for internationalization)

These can be used as templates to build your plugin's configuration schema and UI.

Define Declarative UI (Optional)

The Avro schema can include a $ui field to define how configuration items should be rendered in the EMQX Dashboard. Plugin users can edit the configuration through a dynamic, auto-generated form.

There is also an optional internationalization (i18n) config file, located at priv/config_i18n.json. This file is structured as key-value pairs, for example:

json
{
  "$msgid": {
    "zh": "消息",
    "en": "Message"
  }
}

To support multiple languages in field names, descriptions, validation rule messages, and other UI elements in the $ui configuration, use $msgid prefixed with $ in the relevant UI configurations.

Configuration Item Descriptions

Declarative UI components enable dynamic form rendering within the Dashboard, accommodating a variety of field types and custom components. Below is a description of the available components and their configurations.

  • component Required. Specifies the component type for displaying and configuring data of different values and types. Supported components include:

    Component NameDescription
    inputText input box for short texts or strings
    input-passwordPassword input box that conceals input
    input-numberNumeric input box allowing only numeric input
    input-textareaText area for longer text entries
    input-arrayArray input box for comma-separated values, supporting string and numeric arrays
    switchToggle switch for boolean values
    selectDropdown selection box for enumerated types
    code-editorCode editor for specific formats (e.g., SQL, JSON)
    key-value-editorEditor for editing key-value pairs in Avro maps
    maps-editorEditor for editing object arrays in Avro objects
  • label Required. Defines the field's label or name, supports $msgid for internationalization. If i18n is not configured, the original text will be displayed directly.

  • description Optional. Provides a detailed description of the field, supports $msgid for internationalization. If i18n is not configured, the original text will be displayed directly.

  • flex Required. Defines the proportion of the field in the grid layout; a full grid (24) spans an entire row, while a half grid (12) covers half a row.

  • required Optional. Indicates whether the field is mandatory.

  • format (Applicable only for code-editor component) Optional. Specifies the supported data formats, such as sql or json.

  • options (Applicable only for select component) Optional. Lists the selectable options, aligned with the symbols in the Avro Schema. Example:

    json
    [
      {
        "label": "$mysql",
        "value": "MySQL"
      },
      {
        "label": "$pgsql",
        "value": "postgreSQL"
      }
    ]
  • items (Applicable only for maps-editor component) Optional. When using the maps-editor component, specify the field name and description of the items in the form. For example:

    json
    {
      "items": {
        "optionName": {
          "label": "$optionNameLabel",
          "description": "$optionDesc",
          "type": "string"
        },
        "optionValue": {
          "label": "$optionValueLabel",
          "description": "$optionValueDesc",
          "type": "string"
        }
      }
    }
  • rules Optional. Defines validation rules for the field, where multiple rules can be configured. Supported types include:

    • pattern: Requires a regular expression for validation.
    • range: Validates numeric input within a specified range. This validation can be configured with both a minimum value (min) and a maximum value (max), which can be set either together or independently.
    • length: Validates the character count of input, ensuring it falls within a specified range. This validation rule allows for the configuration of both a minimum length (minLength) and a maximum length (maxLength), which can be set either together or individually.
    • message: Specifies an error message to display when validation fails. This supports internationalization using $msgid to accommodate multiple languages.

Example Validation Rules:

The following are several example snippets. For more detailed examples, refer to priv/config_schema.avsc.example:

json
{
    "rules": [
    {
      "type": "pattern",
      "pattern": "^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])(\\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9]))*$",
      "message": "$hostname_validate"
    }
  ]
}
json
{
    "rules": [
    {
      "type": "range",
      "min": 1,
      "max": 65535,
      "message": "$port_range_validate"
    }
  ]
}
json
{
    "rules": [
    {
      "type": "length",
      "minLength": 8,
      "maxLength": 128,
      "message": "$password_length_validate"
    },
    {
      "type": "pattern",
      "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]*$",
      "message": "$password_validate"
    }
  ]
}

Including Avro Schema and i18n files in your plugin package ensures they are incorporated during plugin compilation and packaging. You can use the emqx_plugins:get_config/1,2,3,4 function in your plugin code to retrieve configuration settings.

OSZAR »