Build a Serverless app using Go and Azure Functions
By: Abhishek Gupta
Tags: Go, Cloud, Serverless, Azure, Azure Functions

Build a Serverless app using Go and Azure Functions

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

in Azure Functions Understand what’s going on behind the scenes with a brief code walk through

Learn how to setup 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!

What are Custom Handlers?

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 web server of the Custom Handler (the function) along with a payload that contains 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.

Overview

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)

Application structure

Let’s look at the how the app is setup. this is as defined in the doc

.

├── cmd

│ └── main.go

├── funcy

│ └── function.json

├── go.mod

├── host.json

└── pkg

└── function

├── function.go

├── giphy.go

└── slack.go

The function.json file is a located in a folder whose name is used the function name (this is by convention)

{

"bindings": [

{

"type": "httpTrigger",

"direction": "in",

"name": "req",

"methods": [

"get",

"post"

]

},

{

"type": "http",

"direction": "out",

"name": "res"

}

]

}

host.json tells the Functions host where to send requests by pointing to a web server capable of processing HTTP events. Notice the customHandler.description.defaultExecutablePath which defines that go_funcy is the name of the executable that'll be used to run the web server. "enableForwardingHttpRequest": true ensures that the raw HTTP data is sent to the custom handlers without any modifications

{

"version": "2.0",

"extensionBundle": {

"id": "Microsoft.Azure.Functions.ExtensionBundle",

"version": "[1.*, 2.0.0)"

},

"customHandler": {

"description": {

"defaultExecutablePath": "go_funcy"

},

"enableForwardingHttpRequest": true

},

"logging": {

"logLevel": {

"default": "Trace"

}

}

}