Posting Form Content to a Slack Channel via an AWS Lambda Function

Websites based on Scrivito are single-page applications, meaning that they don’t require a server for having pages rendered or business logic executed. They are serverless JavaScript applications running in the user’s browser, which makes them fast for visitors and frees their developers from constantly having to monitor the server’s health.

However, there are situations in which a serverless web app needs to make use of remote machinery: sending e-mails, communicating with a CRM, delivering ads, processing form content, posting messages to corporate communication tools such as Slack, and many more.

In all of these cases, the single-page app either doesn’t have the means to perform the task, or you simply don’t want to host your valuable access credentials on the client side. But you’re also reluctant to hire and operate a server for any such subordinate task. This is what serverless computing (or serverless infrastructure) services such as AWS Lambda were made for.

In this guide, we’re going to take a closer look at AWS Lambda and describe how to implement a delicate process: After a contact form on a company’s website has been submitted, the form data should be sent to one of their Slack channels to keep the staff informed. To not expose the Slack channel to the website user, we’ll use an AWS Lambda as a middleman.

For not having to switch back and forth, we’ll first configure the Slack integration to obtain the URL for sending messages to a specific channel. After that, we’ll set up the AWS Lambda and an AWS API Gateway for it. Only then, the form for sending contact requests is taken care of. 

Create the Slack webhook

For posting messages to a Slack channel, an incoming webhook needs to be obtained. Such a webhook is nothing more than a special URL identifying the account and channel concerned. No credentials are involved, so it’s essential to conceal the URL from the public. To have one generated for you, proceed as follows:

  • Log into your Slack account.
  • Open the Incoming WebHooks page.
  • If you don’t have permission to add a (webhook) configuration, request it. Open the URL you are given.
  • Click “Add Configuration”.
  • Select the channel you wish to send messages to and click “Add Incoming WebHooks integration”.

You will then be given the URL you can use in your Lambda post requests:

Further down this form, you can customize the name and description of your webhook if you wish.

Provide the Lambda

An AWS Lambda is code one provides to have it executed at specific events, e.g. when a form on a web page is submitted. This is exactly what we require here. To be able to trigger a Lambda by means of a request, an AWS API gateway needs to be created for it (we’ll explain this in more detail below). You can define an AWS Lambda in the corresponding section of your AWS account.

We named our Lambda “contactRequest” and will feed it with JSON-formatted data using post requests. The latter details are important because our code needs to extract the data from the incoming requests.

Our Lambda first creates the message we want to have posted to the Slack channel. Slack expects the contents of incoming WebHook requests (the payload) to be a JSON object containing the message as a string named text. We are putting it together mainly by concatenating the contents of the incoming post request, the event triggered by submitting the contact request form.

This is our AWS Lambda:

Copy
// "contactRequest" Lambda (node.js based):

const https = require('https');

exports.handler = (event, context, callback) => {
  const payload = JSON.stringify({
    text: `Message sent by ${event.name} (${event.email}):\n ${event.message}`,
  });
  
  const options = {
    hostname: "hooks.slack.com",
    method: "POST",
    path: "/services/ABC1234/CDE5677/SomeSecret123",
  };
  
  const req = https.request(options,
      (res) => res.on("data", () => callback(null, "OK")))
  req.on("error", (error) => callback(JSON.stringify(error)));
  req.write(payload);
  req.end();
}

After setting up the Slack message, the request details are defined. Replace the path inside the options object with the URL path of your Slack WebHook. Next, the request is created, including the messages to be returned on success and error. Then, finally, the request with our payload is issued.

Attach the Lambda to an AWS API Gateway  

As indicated, to be able to have the above code executed, it needs to be made available as a resource in your AWS API Gateway settings.

We named the resource “contact_requests”. Since the Lambda is going to be called using a post request after submitting the contact form, we configured the POST action for it.

The request response can be configured, too. Here, we simply stick to the default response settings and return an empty JSON object:

After setting the “Integration Request” type to LAMBDA and selecting the above “contactRequest” Lambda using the rightmost box, copy the URL from the box titled “Method Request” for pasting it to the form we’ll develop next.

Provide the form

Now that we have the endpoint to which form data can be sent, let’s outline how this can be done with Scrivito. We want to pass the submitted form data to the above AWS Lambda to have it forwarded to Slack.

We are going to use the form widget discussed in Creating a Form Widget that Saves the Input to a Google Sheet as a basis and only adapt its component here. It uses event handlers to save the contents of the form fields to state variables. For our use case, next to  name and email, a message state variable is used. The sent and error states are for controlling how to render the form, depending on whether it has been submitted or not.

Copy
class ContactFormWidget extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      name: '',
      email: '',
      message: '',
      sent: false,
      error: false,
    };

  onInputChange(e) {
    this.setState({ [e.target.name]: e.target.value })
  }

  onFormSubmit(e) {
    e.preventDefault();
    const gatewayUrl = 'https://your-api-gateway-url';
    fetch(gatewayUrl, {
      method: "POST",
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        name: {this.state.name},
        email: {this.state.email},
        message: {this.state.message}
    }).then(
      () => { this.setState({ sent: true }); },
      () => { this.setState({ error: true }); }  }
    );
  }

  render () {
    // Render the form
  }
}

Apart from the render method, all that’s missing is the Gateway API URL of the Lambda you can simply paste as the value of gatewayUrl.

As you can see, the onFormSubmit event handler requests our URL using the POST method, and the relevant state variable values are handed over in JSON.

To complete the form widget, provide the component’s render method as well as the widget class and the editing configuration, like it’s done in Creating a Form Widget that Saves the Input to a Google Sheet.

Final words

Many web-based collaboration, communication, planning, development, etc. tools have integration interfaces for interacting with them. In this tutorial, we presented a solution to just one of the many use cases in which  integrating a Scrivito web app with such tools widens their application area to the benefit of their users.

The underlying principle is always the same: Respond to events by sending the information to be brought to attention to where you want it to show up or trigger some action.

Happy integrating!