Skip to main content

Plugins Development Guide

Plugins are a way to extend the functionality of the Answer project. You can create your own plugins to meet your own needs.

tip

Viewing the official plugin code will make you to quickly understand and learn plugin development.

info

Recommended: Use the official scaffolding tool create-answer-plugin to create and manage plugins. It automates most of the setup process, including file generation, Go module configuration, and plugin installation.

Introduction

Plugin template types

Currently we have three types of plugins:

  • Backend plugin
  • Standard UI plugin
  • Builtin plugin

Plugin type

We classify plugins into different types. Different types of plugins have different functions. Plugins of the same type have the same effect, but are implemented differently.

Plugin NameTemplate TypeDescription
ConnectorBackend PluginThe Connector plugin helps us to implement third-party login functionality
StorageBackend PluginThe Storage plugin helps us to upload files to third-party storage.
CacheBackend PluginSupport for using different caching middleware.
SearchBackend PluginSupport for using search engines to speed up the search for question answers.
User CenterBackend PluginUsing the third-party user system to manage users.
NotificationBackend PluginThe Notification plugin helps us to send messages to third-party notification systems.
RouteStandard UI PluginProvides support for custom routing.
EditorStandard UI PluginSupports extending the markdown editor's toolbar.
CaptchaStandard UI PluginProvides support for captcha.
ReviewerBackend PluginAllows customizing the reviewer functionality.
FilterBackend PluginFilter out illegal questions or answers. (coming soon)
RenderStandard UI PluginParsers for different content formats. (coming soon)

Create a Plugin

info

The name field in package.json is the name of the package we add dependencies to; do not use _ to connect this field naming, please use -; for example:

"editor-chart" ✅
"editor_chart" ❌

The easiest way to create a plugin is using the official scaffolding tool:

# Install the tool globally (optional)
npm install -g create-answer-plugin
# or
pnpm add -g create-answer-plugin

# Or use npx directly (recommended)
npx create-answer-plugin create <pluginName>
# or use the alias
npx answer-plugin create <pluginName>
# or use the simplified form
npx answer-plugin <pluginName>

Note: The package name is create-answer-plugin, but you can use either create-answer-plugin or answer-plugin as the command (both work!).

The tool will:

  1. Guide you through an interactive wizard to select the plugin type
  2. Generate all required files with the correct structure
  3. Create the Go wrapper file (required for Backend plugins)
  4. Set up proper go.mod with all dependencies
  5. Generate i18n files with the correct structure

Options:

  • pluginName (optional): Pre-fill the plugin name
  • --path, -p: Path to Answer project (root directory). If not specified, defaults to current directory.

Example:

# Navigate to your Answer project root
cd /path/to/answer

# Create a plugin
npx create-answer-plugin create my-plugin
# or with path option
npx create-answer-plugin create my-plugin --path /path/to/answer
# Select: Standard UI Plugin → Route
# Enter route path: /hello

The plugin will be created in ui/src/plugins/my-plugin/ (note: plugins is plural).

Manual Creation

If you prefer to create plugins manually:

  1. Go to the ui > src > plugins directory of the project (note: plugins is plural).

  2. Create your plugin directory and files following the structure of existing plugins.

Run the Plugin

The easiest way to install a plugin is using the scaffolding tool's install command:

# Navigate to your Answer project root
cd /path/to/answer

# Install a specific plugin (automatically handles registration)
npx create-answer-plugin install my-plugin
# or
npx answer-plugin install my-plugin
# or with path option
npx answer-plugin install my-plugin --path /path/to/answer

# Install all not installed plugins
npx create-answer-plugin install

Options:

  • plugins (optional): Plugin names to install (defaults to all not installed plugins)
  • --path, -p: Path to Answer project (defaults to current directory)

The install command automatically:

  • ✅ Adds plugin import to cmd/answer/main.go
  • ✅ Adds replace directive to go.mod
  • ✅ Runs go mod tidy
  • ✅ Merges i18n resources using go run ./cmd/answer/main.go i18n

List Plugins

List all plugins in the Answer project:

# List all plugins
npx create-answer-plugin list
# or
npx answer-plugin list
# or with path option
npx answer-plugin list /path/to/answer

Options:

  • path (optional): Path to Answer project (defaults to current directory)

Uninstall Plugins

Uninstall plugins from the Answer project:

# Uninstall all installed plugins
npx create-answer-plugin uninstall
# or
npx answer-plugin uninstall

# Uninstall specific plugins
npx create-answer-plugin uninstall my-plugin another-plugin
# or with path option
npx answer-plugin uninstall my-plugin --path /path/to/answer

Options:

  • plugins (optional): Plugin names to uninstall (defaults to all installed plugins)
  • --path, -p: Path to Answer project (defaults to current directory)

The uninstall command automatically:

  • ✅ Removes plugin import from cmd/answer/main.go
  • ✅ Removes replace directive from go.mod
  • ✅ Runs go mod tidy
  • ✅ Updates i18n resources

Run the Backend Plugin

  1. Install the plugin using the scaffolding tool (see above).

  2. Build the frontend:

    cd ui
    pnpm pre-install
    pnpm build
    cd ..
  3. Merge i18n resources (if not done automatically):

    go run ./cmd/answer/main.go i18n
  4. Start the project:

    go run cmd/answer/main.go run -C ./answer-data

Manual Installation

If you prefer to install manually:

  1. First, build the frontend:

    cd ui
    pnpm pre-install
    pnpm build
    cd ..
  2. In the cmd > answer > main.go file, import your plugin:

    import (
    answercmd "github.com/apache/answer/cmd"

    // Import the plugins
    _ "github.com/apache/answer/ui/src/plugins/my-plugin"
    )
  3. Use go mod edit to add the plugin to the go.mod file:

    go mod edit -replace=github.com/apache/answer/ui/src/plugins/my-plugin=./ui/src/plugins/my-plugin
  4. Update the dependencies:

    go mod tidy
  5. Merge i18n resources:

    go run ./cmd/answer/main.go i18n
  6. Start the project:

    go run cmd/answer/main.go run -C ./answer-data

Run the Standard UI Plugin

  1. Install the plugin using the scaffolding tool:

    cd /path/to/answer
    npx create-answer-plugin install my-plugin
  2. Go to the ui directory and install dependencies:

    cd ui
    pnpm pre-install
  3. Build the frontend:

    pnpm build
  4. For development, start the dev server:

    pnpm start
  5. Merge i18n resources (if not done automatically):

    cd ..
    go run ./cmd/answer/main.go i18n

Manual Installation

  1. Go to the ui directory.

  2. Install the dependencies:

    pnpm pre-install
  3. Build the frontend:

    pnpm build
  4. For development, start the dev server:

    pnpm start
  5. Refer to the Run the Backend Plugin section and manually add the plugin to the project (import in main.go, add replace directive, etc.).

Backend Plugin Development

Implement the Base interface

The Base interface contains basic information about the plugin and is used to display.

// Info presents the plugin information
type Info struct {
Name Translator
SlugName string
Description Translator
Author string
Version string
Link string
}

// Base is the base plugin
type Base interface {
// Info returns the plugin information
Info() Info
}
caution

The SlugName of the plugin must be unique. Two plugins with the same SlugName will panic when registering.

Implement the function interface

note

Different plugin types require different interfaces of implementation.

For example, following is the Connector plugin interface.

type Connector interface {
Base

// ConnectorLogoSVG presents the logo in svg format
ConnectorLogoSVG() string

// ConnectorName presents the name of the connector
// e.g. Facebook, Twitter, Instagram
ConnectorName() Translator

// ConnectorSlugName presents the slug name of the connector
// Please use lowercase and hyphen as the separator
// e.g. facebook, twitter, instagram
ConnectorSlugName() string

// ConnectorSender presents the sender of the connector
// It handles the start endpoint of the connector
// receiverURL is the whole URL of the receiver
ConnectorSender(ctx *GinContext, receiverURL string) (redirectURL string)

// ConnectorReceiver presents the receiver of the connector
// It handles the callback endpoint of the connector, and returns the
ConnectorReceiver(ctx *GinContext, receiverURL string) (userInfo ExternalLoginUserInfo, err error)
}
tip

Translator is a struct for translation. Please refer to the documentation for details.

Implement the configuration interface

For details on the description of each configuration item, please refer to the documentation.

type Config interface {
Base

// ConfigFields returns the list of config fields
ConfigFields() []ConfigField

// ConfigReceiver receives the config data, it calls when the config is saved or initialized.
// We recommend to unmarshal the data to a struct, and then use the struct to do something.
// The config is encoded in JSON format.
// It depends on the definition of ConfigFields.
ConfigReceiver(config []byte) error
}

Register initialization function

import "github.com/apache/answer/plugin"

func init() {
plugin.Register(&GitHubConnector{
Config: &GitHubConnectorConfig{},
})
}

Standard UI plugin Development

The default configuration is as follows:

slug_name: <slug_name> 
type: <type>
version: 0.0.1
author:

import i18nConfig from './i18n';
import Component from './Component';
import info from './info.yaml';

export default {
info: {
slug_name: info.slug_name,
type: info.type,
},
i18nConfig,
component: Component,
};

Among them, typeslug_name and component are required fields. i18nConfig and hooks are optional fields.

Currently the front end supports the following types of plugins:

  • editor
  • route
  • captcha

Editor plugin

Refer to editor-chart for details.

Route plugin

The plugin configuration of the routing type adds the route field to the configuration file.

slug_name: <slug_name>
route: /<route>
type: route
version: 0.0.1
author:

import i18nConfig from './i18n';
import Component from './Component';
import info from './info.yaml';

export default {
info: {
slug_name: info.slug_name,
type: info.type,
route: info.route,
},
i18nConfig,
component: Component,
};

Captcha plugin

Refer to captcha-basic for details.

Builtin plugin Development

It is not so different from React component, this plugin is more suitable for the following scenarios:

  1. There are complex business logics that cannot be separated from the code (such as Oauth).
  2. Some back-end plugins require UI support for business purposes (such as Search).
  3. This plugin has extremely low requirements for developers and requires no additional configuration work.

How to develop builtin plugin

  1. Get familiar with the directory structure. Go to the ui/src/plugins/builtin directory and create a directory, such as Demo. Then refer to the existing plugins to create the necessary files to start development.
// ui/src/plugins/builtin
.
├── ...
├── Demo
├── i18n (language file)
├── en_US.yaml (default language required)
├── index.ts (required)
├── zh_CN.ts (any language you want to provide)
├── index.tsx (component required)
├── info.yaml (plugin information required)
├── services.ts (api)
  1. Export the plugins you have just defined in the plugins list file plugins/builtin/index.ts
import Demo from './Demo'

export default {
...(exists plugins),
Demo,
};
  1. Now you can use the PluginRender component to render the just-defined plugin where you want it!
  <PluginRender
type="connector"
slug_name="third_party_connector"
/>
  1. Publish plugin: initiate the PR process normally and describe the plugin function and scope of influence in detail.