Learn AWS API Gateway with the Slack Police – 2nd Edition

Chris Kalafarski
10 min readMay 19, 2018

--

This is an update to an earlier story of the same name. Many aspects of the Slack API and AWS serverless cloud have changed since that story was published. Each edition solves the same problem in significantly different ways. In the context of learning API Gateway, you should read and understand both approaches.

For an even more in depth look at API Gateway, please see this article written by Alex DeBrie from April 2019.

I bet you’re familiar with those times where someone asks a question in #general and it leads down a rabbit hole of lunch decisions or questions about subdomains. You’re sitting there thinking, “isn’t there a #nerds channel for this kind of stuff?” The answer, of course, is yes, but you don’t want to be the one to put your boss on blast.

It’d be nice if there were some sort of…Slack Police. You could just call the Slack Police and they’d get the riffraff under control.

Not everyone likes cheese on their burgers.

There are many ways to make this happen. Here’s one, which may be educational if you’re interested in AWS’ API Gateway or Lambda functions.

Slack Integrations

The Slack Police is really just a slash command that any user can trigger. The command takes an optional bit of input, and then sends a message back to the channel where the command was triggered.

The Slack API and apps platform have matured significantly over time. In general, Slack has shifted away from one-off or ad hoc integrations, towards an app-centric approach. Slack apps are powerful, and offer a lot of very useful features, security, and conveniences. If you are building a Slack integration that is even just a little bit more complex than something like Slack Police, you should consider creating it as an app.

Slash commands, whether stand alone or as part of a Slack app, are still a primary integration point for Slack, though. Not every interaction in Slack needs a bot user that lives along side human users. Slack Police is a great example of where a slash command fits into the Slack ecosystem — a user issues a command, and something happens immediately, and that’s the end of the story. There’s no state, authentication, permissions, etc.

The first thing to do to create your own Slack Police is setup a new slash command integration for your Slack team or workspace. This could be done as part of a Slack app, but for simplicity we’ll do it as a stand alone custom integration.

Set the Command to whatever you’d like, and just put a placeholder in the URL for now. Everything else you can leave at the default, though you may want to specify the Autocomplete help text or Descriptive Label. Also make a note of the Token, since we’ll need it a little later.

Slash commands work by sending an HTTP request (in this case a POST request) to a URL that you define. Once that endpoint (which we will build in a few moments for Slack Police) gets the request, there are a few different ways to proceed.

If the endpoint responds with either a plain text string or JSON-formatted Slack message data, the user who issued the slash command will receive that message. When the endpoint responds with a message in this way only the user who triggered the command will see that message (in Slack they will see a little note; something like “Only you can see this message”). This can be very useful for some types of slash commands, and makes development really simple in those cases.

For Slack Police, though, we want the the response to be visible to the everyone in the channel. When you return a JSON message response, there is an optional attribute called response_type that you can send with the message, which has two possible values: ephemeral and in_channel. Ephemeral is the same as not including a response_type. In_channel, as the name suggests, indicates that the response should be visible in the channel (i.e., to everyone in the channel). That sounds like what we’re looking for with Slack Police! Except…when the response_type of a slash command response message is set to in_channel, the command itself also becomes visible to the entire channel.

We want Slack Police to be anonymous, so this actually doesn’t work for us. It’s, again, a very useful way of creating interactions with Slack, and in many cases will be what you want. For this particular example, though, we’ll need to figure out another way to get the Slack Police message into Slack, other than simple returning it from the initial slash command HTTP request.

The easiest way to get an arbitrary message back into Slack is with an Incoming Webhook. The Webhook gives us a URL that some JSON can get sent to, and it will get posted to a channel as a message.

We still need the endpoint that’s handling the slash command request to respond with something (an empty HTTP 200 response), so that Slack knows the command was processed successfully. But we can also have the endpoint use an Incoming Webhook to send our Slack Police message.

So to recap:

  1. A user will trigger a slash command in Slack
  2. Slack will send an HTTP post request to our slash command endpoint
  3. The endpoint will generate a Slack message and send it back to Slack via an Incoming Webhook with another HTTP request
  4. The endpoint will return an empty response to the original slash command request
In most cases the response will be blank, but if the command or webhook fail we can send back an error message that only the user who triggered the command will see.

To create the Incoming Webhook, once again, start by creating a new custom integration in Slack. What you choose for Post to Channel doesn’t really matter. We want the message to get sent back to whichever channel the command came from, and that will get set when the Incoming Webhook payload gets sent. Setting it to @slackbot is a good failsafe. The Descriptive Label, Customize Name, and Customize Icon can be whatever you’d like. You’ll need the Webhook URL in a few minutes, so keep it handy.

That’s nearly all the work that needs to be done in Slack. The rest of the job is creating an endpoint somewhere that the slash command can hit, and that can use the Webhook to send messages back. Let’s use API Gateway and Lambda to build that out.

Before diving into the setup of API Gateway and AWS Lambda for Slack Police, it’s worth mentioning that the AWS Serverless Application Model (SAM) is a great way of developing and deploying applications and services that use API Gateway and Lambda. This article won’t strictly cover how to use SAM to develop and deploy Slack Police, but it would be a good way of doing so. I’m going to cheat and use the AWS Console to set up the needed resources, but for production systems that should largely be avoided.

API Gateway

API Gateway lets you define HTTP endpoints that are backed by various services. Since the slash command is configured to POST requests to its URL, we will need a POST method in the API Gateway.

When API Gateway integration with AWS Lambda was first introduced, there was much less flexibility around how much of the heavy lifting the Lambda function could do. A lot of configuration was needed to process and produce HTTP headers and data, and ensure that the event data that did get sent to the Lambda function was properly formatted. (See the 1st edition of this article to compare the old way of building out this API. That writeup is still a good reference for how to use API Gateway with traditional Lambda function integrations.)

Now, though, we have the ability to create API Gateway Lambda Proxy Integrations, which moves the heavy lifting from the API Gateway to the Lambda function. Essentially, the raw HTTP request data is proxied to the function, and the function returns a complete set of HTTP response data, which the API Gateway sends out as its response.

Choosing where to do the work is now up to the developer, and there’s no one correct answer. In many cases, it still makes sense to leave the web/API logic in API Gateway; it’s the service designed for building HTTP APIs. Moving web logic into a Lambda can be more convenient or more comfortable (“I already know node; that’s easier for me to deal with than configuring API Gateway”), but now you have API logic and business logic in the same place.

How you decided to handle that choice will be case-by-case. For Slack Police, the entire setup is so simple, and the service itself is so unimportant, that keeping everything in one spot is easier and makes the app more maintainable.

So let’s start by creating a new API if needed, and then a new Method. There’s no Lambda function to connect the method to yet. For now select Mock Integration, just so we can do some initial setup.

You’ll see a flow diagram that represents this API endpoint. Each part represents a stage in the lifecycle of each request/response that this method will handle. The Client is Slack. The Method Request is the slash command HTTP post request. The Integration Request is the request that API Gateway will make to whatever service is backing this method (Lambda, etc). API Gateway gives us the opportunity to massage and transform the data from the client before it gets sent to the integration, hence the two-stage approach. The response works similarly, with an Integration Response coming back from the backing service, and then ultimately a Method Response that API Gateway returns to the client.

If a four-step process seems overly complicated for something as simple as Slack Police, that’s because it probably is. We really don’t need any of the features that API Gateway offers, which is why we’ve opted to use it simply to proxy the requests to a Lambda function. For more advanced applications, the request authorization, response pattern matching, etc that API Gateway allows for will be very handy. Take some time to learn about what each of those four stages of the life cycle allows you to do to get a good grasp on how to make best use of the service.

That mostly wraps up what needs to be done in API Gateway; there’s very little configuration necessary when using Lambda proxy integrations. Once the Lambda function is set up, the Integration Request will need to be switched from Mock Integration to the function.

AWS Lambda

The actual program behind the Slack Police is the Lambda function that takes the input from the slash command and sends the Incoming Webhook to get a message back into Slack.

Building Lambda functions is fairly straightforward, and there are many good resources.

You can find my version of the function in this gist.

Remember to add the slash command token and Webhook URL from the Slack integrations you setup above as environment variables.

There are a few interesting bits to look at.

return { statusCode: 200, headers: {}, body: ‘’ };

This is the object that gets returned from the Lambda function to API Gateway. Since we opted for a proxy function, we are returning an entire HTTP response, which API Gateway will simply pass along as the response to the original request.

if (payload.token !== SLACK_VERIFICATION_TOKEN) {
// Invalid Slack verification token on payload
throw 'Invalid verification token';
}

The slash command sends the Slack verification token, so we can confirm that any request being made is from a trusted source (i.e. Slack). If the token were ever compromised, you would rotate it in the Slack integration settings and update the value in the Lambda function.

postToWebhook(SLACK_INCOMING_WEBHOOK_URL, {
channel: `#${payload.channel_name}`,
text: messageText(payload),
link_names: 1,
mrkdwn: true
});

Setting the channel on the webhook payload overrides the configured Post to Channel integration setting, which is why every Slack Police message isn’t sent to @slackbot, even though that is what was configured. The link_names option determines if channel or user names are parsed and clickable when displayed in Slack.

Save the Lambda function code, and we’re almost done. Set the Integration Request to that Lambda function, deploy the API Gateway, and, lastly, set the URL on your slash command integration settings in Slack (from way up at the top) to the URL of the deployed API Gateway method.

You should now be able to issue a slash command that sends a request to an API Gateway method, which proxies the request as JSON to a Lambda function, which processes the data so it can send a Webhook back to Slack, and also returns a JSON response API Gateway, and responds to the slash command request appropriately, so that either the user is notified of the error, or the channel where the command was triggered posts a message from the Slack Police.

--

--