Build a Serverless App Using Go and Azure Functions
In this article, learn to implement a Slack app as a Serverless backend by using Azure Functions and Go, allowing you to build powerful experiences in Slack.
Join the DZone community and get the full member experience.Join For Free
Webhook backend is a popular use case for FaaS (Functions-as-a-service) platforms. They could be used for many use cases such as sending customer notifications to responding with funny GIFs! Using a Serverless function, it's quite convenient to encapsulate the webhook functionality and expose it in the form of an HTTP endpoint. In this tutorial, you will learn how to implement a Slack app as a Serverless backend using Azure Functions and Go. You can extend the Slack platform and integrate services by implementing custom apps or workflows that have access to the full scope of the platform allowing you to build powerful experiences in Slack.
This is a simpler version of the Giphy for Slack. The original Giphy Slack app works by responding with multiple GIFs in response to a search request. For the sake of simplicity, the function app demonstrated in this post just returns a single (random) image corresponding to a search keyword using the Giphy Random API. This post provides a step-by-step guide to getting the application deployed to Azure Functions and integrating it with your Slack workspace.
In this post, you will:
- Get an overview of Custom Handlers in Azure Functions
- Understand what's going on behind the scenes with a brief code walkthrough
- Learn how to set up the solution using configure Azure Functions and Slack
- and of course, run your Slack app in the workspace!
The backend function logic is written in Go (the code is available on GitHub. Those who have worked with Azure Functions might recall that Go is not one of the language handlers that is supported by default. That's where Custom Handlers come to the rescue!
In a nutshell, a Custom Handler is a lightweight web server that receive events from the Functions host. The only thing you need to implement a Custom Handler in your favorite runtime/language is: HTTP support! This does not mean that Custom handlers are restricted to HTTP triggers only - you are free to use other triggers along with input and output bindings via extension bundles.
Here is a summary of how Custom Handlers work at a high level (the diagram below has been picked from the documentation)
An event trigger (via HTTP, Storage, Event Hubs, etc.) invokes the Functions host. The way Custom Handlers differ from traditional functions is that the Functions host acts as a middle man: it issues a request payload to the webserver of the Custom Handler (the function) along with a payload that contains the trigger, input binding data, and other metadata for the function. The function returns a response back to the Functions host which passes data from the response to the function's output bindings for processing.
Before we dive into the other areas, it might help to understand the nitty-gritty by exploring the code (which is relatively simple by the way)
Let's look at how the app is setup. this is as defined in the doc
function.json file is located in a folder whose name is used as the function name by convention.
host.json tells the Functions host where to send requests by pointing to a web server capable of processing HTTP events. Notice
customHandler.description.defaultExecutablePath , which defines that
go_funcy is the name of the executable that'll be used to run the webserver.
"enableForwardingHttpRequest": true ensures that the raw HTTP data is sent to the custom handlers without any modifications.
pkg directories contain the Go source code. Let's explore this in the next sub-section.
cmd/main.go sets up and starts the HTTP server. Notice that the
/api/funcy endpoint is the one which the Function host sends the request to the custom handler HTTP server.
All the heavy lifting is done in
The first part is to read the request body (from Slack) and ensure its integrity via a signature validation process based on this recipe defined by Slack.
Once we've confirmed that the function has indeed being invoked via Slack, the next part is to extract the search term entered by the (Slack) user.
Lookup for GIFs with the search term by invoking the GIPHY REST API.
Un-marshal the response sent back by the GIPHY API, convert it into a form which Slack can make sense of, and return it. That's it!
matchSignature function if you're interested in checking the signature validation process and look at slack.go, giphy.go (in the
function directory) to see the Go structs used represent information (JSON) being exchanged between various components. These have not been included here to keep this post concise.
Alright, so far, we have covered lots of theory and background info. It's time to get things done! Before you proceed, ensure that you take care of the below-mentioned pre-requisites.
- Download and install Go if you don't have it already.
- Install Azure Functions Core Tools ̶ this will allow you to deploy the function using a CLI (and also run it test and debug it locally).
- Create a Slack workspace if you don't have one.
- Get a GIPHY API key - You need to create a GIHPY account (it's free!) and create an app. Each application you create will have its own API Key.
Please copy down your GIPHY API key as you will be using it later.
The upcoming sections will guide you through the process of deploying the Azure Function and configuring the Slack for the Slash command.
Start by creating a Resource Group to host all the components of the solution.
- Start by searching for Function App in the Azure Portal and click Add.
- Enter the required details: you should select Custom Handler as the Runtime stack.
- In the Hosting section, choose Linux and Consumption (Serverless) for Operating system and Plan type respectively.
- Enable Application Insights (if you need to).
- Review the final settings and click Create to proceed.
- Once the process is complete, the following resource will also be created along with the Function App:
- App Service plan (a Consumption/Serverless plan in this case).
- An Azure Storage account.
- An Azure Application Insights function.
Clone the GitHub repo and build the function.
GOOS=linux is used to build a
Linux executable since we chose a
Linux OS for our Function App.
To deploy, use the Azure Functions core tools CLI.
Once you've deployed, copy the function URL that's returned by the command ̶ you will use it in subsequent steps.
This section will cover the steps you need to execute to set up the Slack application (Slash command) in your workspace:
- Create a Slack app.
- Create a Slash Command.
- Install the app to your workspace.
Click on Create New Command to define your new Slash Command with the required information. Please note that the Request URL field is the one where you will enter the HTTP endpoint of the function, which is nothing but the URL you obtained after deploying the function in the previous section. Once you're done, hit Save to finish.
Once you're done creating the Slash Command, head to your app's settings page, click the Basic Information feature in the navigation menu, choose to Install your app to your workspace, and click Install App to Workspace ̶ this will install the app to your Slack workspace to test your app and generate the tokens you need to interact with the Slack API. As soon as you finish installing the app, the App Credentials will show up on the same page.
Make a note of your app Signing Secret as you'll be using it later.
Make sure to update the Function App configuration to add the Slack Signing Secret (
SLACK_SIGNING_SECRET) and Giphy API key (
GIPHY_API_KEY) ̶ they will be available as environment variables inside the function.
From your Slack workspace, invoke the command
/funcy <search term>. For e.g. try
You should get back a random GIF in return!
Just a recap of what's going on: When you invoke the
/funcy command in Slack, it calls the function, which then interacts Giphy API and finally returning the GIF to the user (if all goes well).
You may see
timeout error from Slack after the first invocation. This is most likely due to
cold start where the function takes a few seconds to bootstrap when you invoke it for the very first time. This, combined with the fact that Slack expects a response in 3 seconds, gives the error message.
There is nothing to worry about. All you need is to retry again and things should be fine!
Once you're done, don't forget to delete the resource group which in turn will delete all the resources created before (Function app, App Service Plan, etc.).
There is nothing stopping you from using Go for your serverless functions on Azure! I hope this turns out to be a fun way to try out Custom Handlers. Let us know what you think!
Published at DZone with permission of Abhishek Gupta, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.