Building a Slack app with Firebase as a backend
In this article we’ll look at how to develop a Slack app (bot, slash-command or just incoming webhooks) completely without a server backend.
By following the example in this article you will be using Slack slash-commands and incoming webhook to implement a simple game that we can call ping pong (not to be confused with the sport that incidentally has the same name).
Each of these sections can be studied separately if you are already familiar with one or more of the others.
If you’re not planning to distribute your app, then the OAuth section is optional. And the complete code for this project is available on Github
Prerequisites
You will need to enable billing on your Firebase project to enable outgoing HTTP requests for your functions, which you will need for Slack communication.
The Blaze plan is perfect since it’s “ pay as you go” and that’s going to be free as long as you stay below the limits (my hobby projects never reach over $0.05/month for my own development needs, so you don’t need to worry about any nasty surprises).
Implementing the OAuth flow
You’ll need to implement the OAuth flow if you’re planning to distribute your app so that other teams can use it. You can skip this section and jump straight to the next if you don’t have a need for this (just hard-code or put your team’s incoming webhook URL in a configuration parameter). Or come back to this topic later if you feel like getting started with the fun stuff first.
Step 1
Register a callback URL to for the Slack OAuth flow to put you into the loop.
Step 2
Provide an “Add to Slack” button on your project website by following the steps in the official documentation.
Step 3
Your callback/redirect method will be called upon both clicking “Authorize” and “Cancel”. Implement handling of both cases.
Your will receive a GET request with the query parameters “code” and “state”. Use these parameters to complete the handshake with Slack. The state parameter is optional but recommended.
In the handshake call to https://slack.com/api/oauth.access you will receive a JSON response looking something similar to this:
Step 4
Store the parts of the information from the JSON response that you want to keep for the team that installed the integration. You need to do that if you want to be able to invoke calls to the team such as incoming webhooks.
Consuming a slash command
Your target Firebase cloud function must reply to Slack within 3000ms (read the docs) so it’s important that you try to keep the amount of work to the minimum in the initial trigger.
The way I choose to do this is to simply save the command in a database path that I treat as a queue of commands. Then there is another cloud function that triggers on new commands in the queue.
Concerning experiences
Even if you keep your work to the minimum, there’s a chance that you’ll run into Slack’s somewhat tight deadline. Even with the suggested strategy above, I have still been able to find myself out of luck by delays in function invocation.
Consuming button actions
In a similar strategy of consuming the commands, the actions are also placed in a queue to be handled by another cloud function.
HTTP Trigger
All interactive Slack message actions are reported to one and the same callback URL. So your function need to handle inspection of the data to find out its context. This example’s HTTP trigger function only support one type of actions.
Once again, the work in the HTTP trigger is kept to a minimum. Although, at the bottom of the function there’s some logic that tries to limit the ping-pong game to maximum three replies. The button changes style for each reply that’s being registered.
As an exercise of further development, this logic could actually be moved down into the database trigger to be more precise about how many replies that has been registered and also making sure that one user can only reply once.
Database trigger
Compared to the handling of the slash command earlier, this example is more clear on why it is necessary to return the HTTP trigger as fast as possible and putting the rest of the task in a queue for processing as soon as possible. The action handling logic includes several reads and writes to the database. In order to stay on the safe side of the narrow 3000ms window to respond, it’s better to dispatch the work to another cloud function than the initial HTTP trigger.
This function takes the users’ click actions and records the first three responses. This is a perfect example of how you can use a Firebase transaction for an instantly consistent (and expected) state.
Finally we send back the results of the game to the Slack team:
Conclusion
This example is way more elaborate the the minimum requirements to get started. Try cutting out all the content of the functions and you’ll see how quick and simple that you can get started.
Firebase cloud functions are ridiculously easy to use and to deploy in comparison to the relatively complex arrangements for implementing HTTP API endpoints on any other hosted cloud server solution. But I find Slack’s annoyingly tight deadline of 3000ms for commands to return a response to be a blocking obstacle for a service to behave reliably and consistent.
Feel free to get in touch with me for any questions or feedback. You’re more than welcome to help and improve the project on Github if you think that it would help others… or just for fun.
Subscribe to my feed for more updates on Firebase development.