Creating Your First App
Getting Started in creating your first ever Rocket.Chat App
Now, that you've understood the basic concepts of the Apps Engine and installed the CLI, you can create an extremely basic RC App and test it out to understand things. To get started, just recall the commands inside the Apps Engine CLI.

Creating a new RocketChat App

Bootstrap Your App

The development tools provide a command to quickly scaffold a new Rocket.Chat App. Simply run rc-apps create , give it some details and a new folder will be created inside the current working directory with a basic app that does nothing but will compile and be packaged in the dist folder.
Let's create a simple app named HelloWorld.
1
[[email protected] apps-engine]$ rc-apps create
2
Let's get started creating your app.
3
We need some information first:
4
5
App Name: HelloWorld
6
App Description: Says Hello World!
7
Author's Name: Jane Doe
8
Author's Home Page: n/a
9
Author's Support Page: n/a
10
Creating a Rocket.Chat App in ./helloworld... done!
Copied!

App description

The basic creation of an App is based on extending the App class from the Rocket.Chat Apps definition library. Your class also has to implement the constructor and optionally the initialize function, for more details on those check the App definition documentation.
The app description file, named app.json, contains basic information about the app. You can check the app-schema.json file for all the detailed information and fields allowed in the app description file.
Let's take a look at our HelloWorld app's app.json file below to understand the structure.
1
{
2
"id": "b0ef0de4-1ee0-4de7-9d16-0f83a75f9dd2",
3
"version": "0.0.1",
4
"requiredApiVersion": "^1.19.0",
5
"iconFile": "icon.png",
6
"author": {
7
"name": "Jane Doe",
8
"homepage": "n/a",
9
"support": "n/a"
10
},
11
"name": "HelloWorld",
12
"nameSlug": "helloworld",
13
"classFile": "HelloWorldApp.ts",
14
"description": "Says Hello World!",
15
"implements": []
16
}
Copied!

Testing RocketChat App

To test your app, you need a Rocket.Chat server running locally on your machine and access to an admin user's credentials.
See Installing Rocket.Chat for Developing to run Rocket.Chat in develop mode. Enable Apps development mode by navigating to Administration > General then scroll down to Apps and click on the True radio button over the Enable development mode.
You can learn more about organizing complex slash commands in our Sub-command pattern recipe
or run it in preview mode with docker using the command:
1
docker run -it --rm -p 3000:3000 -v $(pwd)/rocketdb:/var/lib/mongodb rocketchat/rocket.chat.preview
Copied!
Having the server running, simply run inside the app project's directory:
1
rc-apps deploy --url http://localhost:3000 --username <username> --password <password>
Copied!
Where:
http://localhost:3000 is your local server URL (if you are running in another port, change the 3000 to the appropriate port)
username is the username of your admin user.
password is the password of your admin user.
If you want to update the app deployed in your Rocket.Chat instance after making changes to it, you can run:
1
rc-apps deploy --url http://localhost:3000 --username user_username --password user_password --update
Copied!
After version 1.9 of the App Engine CLI, the --update flag isn't strictly necessary for updating an existing App, you can just run the deploy command without it.

Understanding the App

A Rocket.Chat App is basically a TypeScript file on the root of your project containing a simple class that extends the main App class. The name of this file is in your app.json file under the classFile property.
Let's take a look at our own project's root. You should find a TypeScript file named HelloWorldApp.ts.
Open the HelloWorldApp.ts file. Ignore all the import statements for now. Focus on the main exported class.
1
export class HelloWorldApp extends App {
2
constructor(info: IAppInfo, logger: ILogger, accessors: IAppAccessors) {
3
super(info, logger, accessors)
4
}
5
}
Copied!
The first thing that you should notice is that the class name is the same as the filename. This is intentional.
For the app to compile successfully, you either have to use the same name for the class and the file or default export the main app class like below:
1
export default class HelloWorld extends App {
2
constructor(info: IAppInfo, logger: ILogger, accessors: IAppAccessors) {
3
super(info, logger, accessors)
4
}
5
}
Copied!
The most minimal app wouldn't even have a constructor. For example, you can edit the class to:
1
export class HelloWorldApp extends App {}
Copied!
This will compile just fine, and can also be deployed.
But for a functioning app, you need access to a lot of parent property, for which you have to define a constructor. You also have to call the super constructor. For more details as to why, refer to the TypeScript documentation.
The constructor has three arguments,
  1. 1.
    An IAppInfo object: This object contains basic information about your app, like the name, the version, description, etc. It is private to the App class but its properties can be accessed via different get methods.
  2. 2.
    An ILogger object: The logging interface. You can access this object from your child class by using the getLogger() method.
  3. 3.
    An IAppAccessors object: Object containing all the app accessors. You can access this by using the getAccessors() method in your child class.

Start Developing

Currently, our HelloWorld app does nothing. In this section, we'll make it log Hello, World! in the Rocket.Chat admin panel.
To log something, you first need access to the logger, or more specifically, access to an object of type ILogger. The parent class uses an ILogger object to log stuff to the admin panel. We just need access to that object. Unfortunately, the logger object is private to the App class and so cannot be accessed directly using this.
This can easily be remedied by using the getLogger method provided by the App class. Simply store the logger in a separate object and then it can be reused any time.
Let's do so now.
1
export class HelloWorldApp extends App {
2
private readonly appLogger: ILogger
3
constructor(info: IAppInfo, logger: ILogger, accessors: IAppAccessors) {
4
super(info, logger, accessors)
5
this.appLogger = this.getLogger()
6
}
7
}
Copied!
We have just stored the logger accessor in the appLogger variable. Now we can use it to log anything. Add the following line to the constructor.
1
this.appLogger.debug('Hello, World!')
Copied!
Finally the class file looks like:
1
import {
2
IAppAccessors,
3
ILogger
4
} from '@rocket.chat/apps-engine/definition/accessors'
5
import {App} from '@rocket.chat/apps-engine/definition/App'
6
import {IAppInfo} from '@rocket.chat/apps-engine/definition/metadata'
7
8
export class HelloWorldApp extends App {
9
public appLogger: ILogger
10
constructor(info: IAppInfo, logger: ILogger, accessors: IAppAccessors) {
11
super(info, logger, accessors)
12
this.appLogger = this.getLogger()
13
this.appLogger.debug('Hello, World!')
14
}
15
}
Copied!
You can now deploy it following the instructions provided above.

Checking the logs

Now to verify that it actually is working, you need to check the logs.
  1. 1.
    Log in to your Rocket.Chat server as the admin user.
  2. 2.
    Go to Administration panel.
  3. 3.
    From the side panel, click on Apps.
  4. 4.
    Now you should see your HelloWorld app right there. Click on it.
  5. 5.
    On the right-hand side there is a three-dot button, click on it, and then from the menu click on logs.
Scroll down until you see constructor. When you do, click on it.
Following screen apears:
You just created the simplest Hello World Rocket.Chat app! 😊

Adding a Slashcommand

Logging to the console is good, but we want our app to interact within a room in Rocket.Chat. In this section we'll make our hello world app send a message in the room you currently have open.
To do this, we're going to use a slash command.
A Slashcommand is a way to call the app installed in Rocket.Chat. Your app can have multiple slashcommands and subcommands. In our example, we will add the liftoff slashcommand and it will be called like this by the user inside the chat room:
For this app, let's make the command hello. So you should be able to run /hello to invoke a function of the app.
1
/hello
Copied!
Although you can have everything in a single file, it is not recommended. Better put each physical component of your app in its own file and even better group the logically similar components in their own subdirectories.
For slashcommand related files, we recommend a subdirectory in your project root named Commands, but you are free to choose any other name.
You can create one now in this directory, create a file named HelloWorldCommand.ts.
Enter the following line in the file.
1
import ISlashCommand from "@rocket.chat/apps-engine/definition/slashcommands";
2
3
export class HelloWorldCommand implements ISlashCommand {}
Copied!
A SlashCommand is an instance of some class type that implements the ISlashCommand interface.
In TypeScript, an interface is a description of an object. It specifies what properties or fields an object must or may have.
When a class implements an interface, it makes a promise that it will conform to the interface's object structure specification.
For example, if an interface looks like this,
interface Person { name: string; }
It says any class implementing the Person interface must have a property of type string called name. If it doesn't, the file won't compile.
For more information on TypeScript interfaces, read the official documentation.
ISlashCommand being a non-empty interface, the above HelloWorldCommand class is incomplete. It must fulfill its promise of conforming to the structure.
Let's now look at what properties our HelloWorldCommand class now must define
command
This is a public variable of type string, it is the command name of the command you are to enter after the slash. In this case, the value is hello.
1
public command: string = 'hello';
Copied!
i18nDescription
Description of the app in i18n string.
1
public i18nDescription: string = 'Just says Hello to the World!';
Copied!
i18nParamsExample
Parameter examples in i18n string.
1
public i18nParamsExample = '' // we have no params
Copied!
providesPreview
Tells the app whether this command provides command preview or not. We'll talk about command preview in a later section. For now, please use false.
1
public providesPreview: boolean = false;
Copied!
executor
This is the method that will be invoked when a user executes the slashcommand. This is where the whole logic and action will take place. When the method is called, it is passed a couple of objects. These arguments define the function signature. It is also documented in the ISlashCommand interface.
1
public async executor(
2
context: SlashCommandContext,
3
read: IRead,
4
modify: IModify,
5
http: IHttp,
6
persis: IPersistence
7
): Promise<void> {}
Copied!
The executor above doesn't do anything, yet.
Let's talk about what each parameter type are used for.
  • SlashCommandContext → An Object containing information about the context around which the command was run. E.g. the user executing the command, the room where the command was executed, command parameters, etc.
  • IRead → This is an object that provides read-only access to the current environment. E.g. room details, user details, app settings, etc.
  • IModify → An Object that gives you the ability to modify the environment, or actions that change the environment in some way. E.g. sending a message, creating a room, deleting a room, etc.
IHttp and IPersistence are discussed in a different section.
Your HelloWorldCommand class should look like the following now.
1
export class HelloWorldCommand implements ISlashCommand {
2
public command = "hello";
3
public i18nDescription = "Just says Hello to the World!";
4
public providesPreview = false;
5
public i18nParamsExample = "";
6
7
public async executor(
8
context: SlashCommandContext,
9
read: IRead,
10
modify: IModify,
11
http: IHttp,
12
persis: IPersistence
13
): Promise<void> {}
14
}
Copied!

Creating a message

Creating any resource in Apps-Engine is a three-step process.
  1. 1.
    First, you need a creator object, namely an instance of IModifyCreator.
  2. 2.
    Next for the resource that you want to create, you need a resource builder. A resource builder is basically an object representation of the resource.
  3. 3.
    Finally, call the finish method provided by the creator object. It will take the template object and use it to actually build the resource on the server side.
You can get the creator object by using the IModify.getCreator method. In the executor method, simply use the modify object to get the creator.
1
const creator: IModifyCreator = modify.getCreator();
Copied!
Now we need to create the resource builder, in this case, a message builder. We're going to use IModifyCreator.startMessage method for this.
This method will take the message template or an object representation of a message.
A message needs at the very least,
  1. 1.
    The text - visible content
  2. 2.
    A sender - who's sending the message
  3. 3.
    A room - where the message is sent.
We can get the room information from the context.
1
const room: IRoom = context.getRoom()
Copied!
We want our app to send the message, so for the sender, we need the app user. To get the app user information, we need to read our environment, for which an instance of IRead or in executor, the read object is going to be useful.
1
const sender: IUser = (await read.getUserReader().getAppUser()) as IUser
Copied!
Awesome, now we can construct the message template with the content or text being simply, 'Hello, World!'
1
const messageTemplate: IMessage = {
2
text: 'Hello, World!',
3
sender,
4
room
5
}
Copied!
Let's now create the message builder.
1
const messageBuilder: IMessageBuilder = creator.startMessage(messageTemplate)
Copied!
To 'create' the resource, i.e. the message, or more intuitively saying, to 'send' the message, all you need to do now is run the IModifyCreator.finish method.
1
await creator.finish(messageBuilder)
Copied!
Combining all these steps, your HelloWorldCommand.ts file looks like:
1
import {
2
IHttp,
3
IMessageBuilder,
4
IModify,
5
IModifyCreator,
6
IPersistence,
7
IRead
8
} from '@rocket.chat/apps-engine/definition/accessors'
9
import {IMessage} from '@rocket.chat/apps-engine/definition/messages'
10
import {IRoom} from '@rocket.chat/apps-engine/definition/rooms'
11
import {
12
ISlashCommand,
13
SlashCommandContext
14
} from '@rocket.chat/apps-engine/definition/slashcommands'
15
import {IUser} from '@rocket.chat/apps-engine/definition/users'
16
17
export class HelloWorldCommand implements ISlashCommand {
18
public command = 'hello'
19
public i18nDescription = ''
20
public providesPreview = false
21
public i18nParamsExample = ''
22
23
public async executor(
24
context: SlashCommandContext,
25
read: IRead,
26
modify: IModify,
27
http: IHttp,
28
persis: IPersistence
29
): Promise<void> {
30
const creator: IModifyCreator = modify.getCreator()
31
const sender: IUser = (await read.getUserReader().getAppUser()) as IUser
32
const room: IRoom = context.getRoom()
33
const messageTemplate: IMessage = {
34
text: 'Hello, World!',
35
sender,
36
room
37
}
38
const messageBuilder: IMessageBuilder = creator.startMessage(messageTemplate)
39
await creator.finish(messageBuilder)
40
}
41
}
Copied!

Registering the slashcommand

Once you have your slashcommand ready, you need to let your app know about it.
You do that by overriding the App.extendConfiguration method.
The method takes an object of type IConfigurationExtend. This object is what we'll have to use to 'extend' our app's configurations.
A configuration is like a feature of an app. They plug into your app.
There are multiple types of configurations, like API endpoints, settings, slashcommands, etc. We will discuss the slashcommands here.
First, let's create our slashcommand. We already have our slashcommand class HelloWorldCommand, but that's just the class, or the template or design of the final object, which is the actual slashcommand.
1
const helloWorldCommand: HelloWorldCommand = new HelloWorldCommand()
Copied!
After adding our slashcomamnd logic, we have to register the slashcommand in our app by extending its configuration:
1
await configuration.slashCommands.provideSlashCommand(helloWorldCommand)
Copied!
The final HelloWorldApp.ts file looks like:
1
import {
2
IAppAccessors,
3
IConfigurationExtend,
4
ILogger
5
} from '@rocket.chat/apps-engine/definition/accessors'
6
import {App} from '@rocket.chat/apps-engine/definition/App'
7
import {IAppInfo} from '@rocket.chat/apps-engine/definition/metadata'
8
import {HelloWorldCommand} from './Commands/HelloWorldCommand'
9
10
export class HelloWorldApp extends App {
11
private readonly appLogger: ILogger
12
constructor(info: IAppInfo, logger: ILogger, accessors: IAppAccessors) {
13
super(info, logger, accessors)
14
}
15
16
public async extendConfiguration(
17
configuration: IConfigurationExtend
18
): Promise<void> {
19
const helloWorldCommand: HelloWorldCommand = new HelloWorldCommand()
20
await configuration.slashCommands.provideSlashCommand(helloWorldCommand)
21
}
22
}
Copied!

Test the App

Your first app is ready. All that's left is for you to test it! Head over to thetesting-the-app-1 section, to deploy your first app to your Rocket.Chat server!