How to Extend Rocket.Chat Functionality Using the Apps-Engine Framework
Developers extend Rocket.Chat functionality by creating sandboxed Apps that interact with the core server through the Apps-Engine framework, utilizing controlled accessors to modify messages, register slash commands, add UI buttons, and expose HTTP endpoints without modifying core code.
Rocket.Chat is an open-source communication platform designed for extensibility through its proprietary Apps-Engine architecture. Rather than modifying core server files, developers package custom behaviors as standalone Rocket.Chat Apps that run in isolated sandboxes. This approach ensures upgrade compatibility while providing deep integration capabilities through well-defined APIs.
Understanding the Apps-Engine Architecture
The extension system centers on the @rocket.chat/apps-engine package, which implements a strict plugin architecture. When the server initializes, it instantiates a singleton AppManager (packages/apps/src/server/AppManager.ts) that orchestrates the entire app lifecycle.
The AppManager performs four critical operations:
- Loads installed app packages from the storage layer
- Compiles each app into a sandboxed runtime via
AppRuntimeManager(Node VM) - Registers listeners, commands, and endpoints through specialized managers (
AppListenerManager,AppSlashCommandManager,AppApiManager,UIActionButtonManager) - Handles lifecycle events (
onInstall,onEnable,onDisable,onUninstall,onUpdate) by delegating to the user-defined class extending the abstractAppbase class (packages/apps-engine/src/definition/App.ts)
Core Extension Points for Rocket.Chat Apps
Event-Driven Listeners
Apps register for system events to intercept or react to platform activities. Available events include IPreMessageSentPrevent, IPostMessageSent, and IPreRoomCreate. When the core emits these events, the AppListenerManager invokes the corresponding handler within the app's sandbox.
Slash Commands
Custom commands are registered through configuration.slashCommands.provideSlashCommand(), allowing apps to expose functionality via the message input box.
UI Action Buttons
Developers inject interactive elements into the client interface using configuration.uiActionButtons.provideButton(), handled server-side by UIActionButtonManager (packages/apps/src/server/managers/UIActionButtonManager.ts).
Custom HTTP Endpoints
Apps expose REST APIs through configuration.api.provideApi(), managed by AppApiManager (packages/apps/src/server/managers/AppApiManager.ts), with configurable visibility (public or private).
Data Persistence
The IPersistence accessor allows apps to store private data using persistence.create() and persistence.update(), ensuring isolated storage per app.
Building a Minimal Rocket.Chat App
Every app extends the abstract App class and implements the initialize method to register capabilities. Here is a complete "Hello World" example that demonstrates slash commands and message listeners:
// src/App.ts
import {
IAppAccessors,
IConfigurationExtend,
IEnvironmentRead,
} from '@rocket.chat/apps-engine/definition/accessors';
import { App } from '@rocket.chat/apps-engine/definition/App';
import { IAppInfo } from '@rocket.chat/apps-engine/definition/metadata';
export class HelloWorldApp extends App {
constructor(info: IAppInfo, logger: any, accessors?: IAppAccessors) {
super(info, logger, accessors);
}
public async initialize(configuration: IConfigurationExtend, environment: IEnvironmentRead) {
await this.registerCommand(configuration);
await this.registerListener(configuration);
}
private async registerCommand(configuration: IConfigurationExtend) {
await configuration.slashCommands.provideSlashCommand({
command: 'hello',
i18nDescription: 'Say hello',
providesPreview: false,
executor: async (context, read, modify, http, pers) => {
const { user } = context;
const message = modify.getCreator().startMessage()
.setSender(user)
.setRoom(context.getRoom())
.setText(`👋 Hello, ${user.username}!`);
await modify.getCreator().finish(message);
},
});
}
private async registerListener(configuration: IConfigurationExtend) {
await configuration.messageListeners.provideMessageListener({
eventName: 'IPostMessageSent',
execute: async (context, read, modify) => {
const { message } = context;
if (/thanks/i.test(message.text || '')) {
await modify.getCreator().addReaction(message.id, '👍');
}
},
});
}
}
This implementation leverages the IModify accessor to create messages and reactions, while the IRead accessor (available in parameters) provides read-only access to rooms and users.
Advanced Extension Patterns
Adding UI Action Buttons
To extend Rocket.Chat functionality with client-side interactions, apps register UI buttons that appear in message contexts:
await configuration.uiActionButtons.provideButton({
appId: this.getID(),
id: 'open-ticket',
label: { en: 'Open Ticket' },
context: ['message'],
action: async (context, read, modification, http, pers) => {
const { message } = context;
const ticketRoom = await modification.getCreator().startRoom()
.setType('c')
.setDisplayName(`Ticket for ${message.sender.username}`)
.setCreator(message.sender)
.finish();
await modification.getNotifier().notifyUser(message.sender, {
rid: ticketRoom.id,
msg: 'Your ticket has been created!',
});
},
});
The UIActionButtonManager processes these registrations and coordinates with the client-side orchestrator (apps/meteor/client/apps/orchestrator.ts) to render the components.
Exposing Custom HTTP Endpoints
Apps can create private or public REST endpoints to integrate external services:
await configuration.api.provideApi({
path: '/external-data',
visibility: ApiVisibility.PRIVATE,
endpoints: [
{
path: '/fetch',
method: HttpMethod.GET,
auth: {
requiredPermissions: [],
},
endpoint: async (request, read, modify, http, pers) => {
const external = await http.get('https://api.example.com/data');
return {
status: HttpStatusCode.OK,
headers: { 'Content-Type': 'application/json' },
content: JSON.stringify(external.data),
};
},
},
],
});
The AppApiManager routes incoming requests to the appropriate sandboxed endpoint, enforcing the specified authentication and permissions.
Key Source Files in the Apps-Engine
Understanding the following source files is essential for developers looking to extend Rocket.Chat functionality:
packages/apps/src/server/AppManager.ts: The core singleton responsible for loading, compiling, and managing the lifecycle of all installed apps.packages/apps-engine/src/definition/App.ts: The abstract base class that every developer-written app must extend.packages/apps/src/server/managers/AppListenerManager.ts: Registers and unregisters event listeners for app callbacks.packages/apps/src/server/managers/AppSlashCommandManager.ts: Handles registration and execution of custom slash commands.packages/apps/src/server/managers/AppApiManager.ts: Manages custom HTTP endpoints exposed by apps.packages/apps/src/server/managers/UIActionButtonManager.ts: Processes UI action button registrations for client rendering.packages/apps/src/server/runtime/AppRuntimeManager.ts: Creates and manages sandboxed Node VM runtimes for app isolation.apps/meteor/client/apps/orchestrator.ts: Client-side bridge that loads UI-kit definitions from server-side apps.
Summary
- Rocket.Chat Apps provide the official mechanism to extend Rocket.Chat functionality without modifying core server code.
- The Apps-Engine architecture uses a sandboxed runtime (Node VM) to ensure security and stability.
- Developers interact with the platform through accessors (
IRead,IModify,IHttp,IPersistence) injected into lifecycle methods. - Extension points include event listeners, slash commands, UI action buttons, and custom HTTP endpoints.
- The AppManager singleton orchestrates app lifecycles while specialized managers handle specific capability registrations.
Frequently Asked Questions
What programming language is used to extend Rocket.Chat functionality?
Rocket.Chat Apps are written in TypeScript (compiled to JavaScript). The Apps-Engine provides type definitions in the @rocket.chat/apps-engine package, ensuring type-safe access to platform APIs and accessors.
How do Rocket.Chat Apps differ from traditional server modifications?
Unlike direct server modifications that alter core files and break during upgrades, Apps run in isolated sandboxes managed by AppRuntimeManager. They interact with the host exclusively through well-defined accessors, ensuring the core server remains stable and upgrade-compatible.
Can Apps interact with external APIs and services?
Yes. The IHttp accessor allows apps to make outbound requests to external services. Additionally, apps can expose their own endpoints via provideApi(), managed by AppApiManager, to receive inbound webhooks or serve data to external systems.
Where are Apps stored and how are they lifecycle-managed?
Apps are stored in the server's database and loaded at startup by AppManager (packages/apps/src/server/AppManager.ts). The manager handles installation, enabling, disabling, updating, and uninstallation by calling the corresponding lifecycle methods (onInstall, onEnable, onDisable, onUninstall, onUpdate) defined in the app's main class.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →