Almost EVERYONE who tried headless systems said they saw benefits. Download the state of CMS now!

Storyblok now on AWS Marketplace: Read more

O’Reilly Report: Decoupled Applications and Composable Web Architectures - Download Now

Empower your teams & get a 582% ROI: See Storyblok's CMS in action

Skip to main content

How to Setup a Serverless Contact Form with AWS Lambda, reCAPTCHA and Storyblok

Try Storyblok

Storyblok is the first headless CMS that works for developers & marketers alike.

  • Home
  • Tutorials
  • How to Setup a Serverless Contact Form with AWS Lambda, reCAPTCHA and Storyblok

In this tutorial I'll explain you how to setup an api endpoint that accepts a JSON to send an email and create an entry in Storyblok's data storage. You can use this for a contact form or a comment form on your dynamic or static website.

The Lambda function also validates the request with Google reCAPTCHA to prevent spam.

Section titled Architectural overview Architectural overview

Let's see how this works:

  1. The user sends the request together with the captcha parameter 'g-recaptcha-response' to the AWS API Gateway
  2. g-recaptcha-response will be validated by reCAPTCHA in the Lambda function
  3. If the validation was successful we'll send and email via AWS SES
  4. Then we will create a content entry with the content type 'form_entry' in a Storyblok folder

AWS Lambda serverless form

Section titled Setup Setup

To make things easy I created a Cloudformation template which you can use to launch the api with a single click. You can find the code of the template at following repository: github.com/storyblok/lambda-form-submission.

Clicking here will bring you to the AWS Cloudformation setup page with a preselected template.

New cloudformation stack

The next screen let's you define the configuration of the Lambda function. Fill out all the parameters:

  • StoryblokOauthTokenYou can generate a Storyblok OAuth token in the "My account" section. The OAuth token let's the Lambda function access the management API of Storyblok to create content entries.
  • StoryblokParentFolderIdThe parent folder id tells the script in which Storyblok folder to create the content entries. When creating the folder define form_entry as default content type and click the checkbox of disabling the visual editor.Form entryAfter creating the folder click on it to get the folder id from the url: /me/spaces/39837/stories/index/THIS_IS_THE_FOLDER_ID
  • StoryblokSpaceIdGet the space id from the url in Storyblok: /me/spaces/THIS_IS_THE_SACE_ID/stories
  • ToEmailAddressDefine the email address where the notifications will be sent.
  • ReCaptchaSecretDefine the reCAPTCHA secret from www.google.com/recaptcha/admin. If you have not created a token yet create one with "Invisible reCAPTCHA" selected.

Cloudformation settings

On the next pages of the Cloudformation stack creation process you can continue with the default values and finally initiate the creation.

Section titled Creating the form Creating the form

After creating the Cloudformation stack we'll setup a simple contact form to test the API.

Section titled The HTML part The HTML part

Paste following code somewhere on your page and replace YOUR_RECAPTCHA_PUBLIC_KEY with the "Site key" from Google reCAPTCHA.

        
      <form id="form">
  <p class="form__errors" style="display: none;">Please fill in all fields.</p>

  <label for="name_input">Name</label><br>
  <input type="text" id="name_input" name="name"><br>
  <label for="message_input">Message</label><br>
  <textarea type="textarea" id="message_input" name="message"></textarea>

  <p class="form__success" style="display: none;">Sent successfully!</p>
  <p class="form__sending" style="display: none;">Sending...</p>

  <button type="submit" class="g-recaptcha form__button"
    data-sitekey="YOUR_RECAPTCHA_PUBLIC_KEY"
    data-callback="handleFormSubmit"
    data-badge="inline">Submit</button>
</form>
    

Section titled The Javascript part The Javascript part

Copy the following Javascript code after the contact form HTML.

        
      <script src='https://www.google.com/recaptcha/api.js' async defer></script>
<script>
  var handleFormSubmit = function () {
    var formApiEndpoint = 'YOUR_API_ENDPOINT'
    var successEl = document.querySelector('.form__success')
    var sendingEl = document.querySelector('.form__sending')
    var errorsEl = document.querySelector('.form__errors')
    var buttonEl = document.querySelector('.form__button')
    var nameInput = document.querySelector('#name_input')
    var messageInput = document.querySelector('#message_input')
    var recaptchaResponse = document.querySelector('#form textarea[name=\'g-recaptcha-response\']')

    successEl.style.display = 'none'
    errorsEl.style.display = 'none'

    if (nameInput.value == '' || messageInput == '') {
      errorsEl.style.display = 'block'
      buttonEl.style.display = 'block'
      sendingEl.style.display = 'none'
    } else {
      var formRequest = new Request(formApiEndpoint, {
        method: 'POST',
        body: JSON.stringify({
          name: nameInput.value,
          message: messageInput.value,
          'g-recaptcha-response': recaptchaResponse.value
        })
      })

      fetch(formRequest)
        .then(function(response) {
          if (response.status === 200) {
            return response.json()
          } else {
            throw new Error('Something went wrong on api server!')
          }
        })
        .then(function(response) {
          successEl.style.display = 'block'
          buttonEl.style.display = 'block'
          sendingEl.style.display = 'none'
          nameInput.value = ''
          messageInput.value = ''
        }).catch(function(error) {
          errorsEl.style.display = 'block'
          buttonEl.style.display = 'block'
          sendingEl.style.display = 'none'
          console.error(error)
        })
    }
  }

  document.querySelector('.form__button').addEventListener('click', function() {
    document.querySelector('.form__sending').style.display = 'block'
    document.querySelector('.form__button').style.display = 'none'
  })
</script>
    

Replace YOUR_API_ENDPOINT with your Amazon API-Gateway url which you can grab at the Outputs section of your Cloudformation stack:

Cloudformation output

Section titled Testing the API Testing the API

Make sure that your domain and email is verified in the Amazon SES (Simple Email Service) management console. Otherwise sending will not work.

If you have setup everything correctly you should receive an email when sending the contact form. Additionally there should be created a content item in the predefined folder in Storyblok.

Section titled Conclusion Conclusion

With this setup you can easily capture any user input and have the data stored in Storyblok for contact forms or a comments system. Thanks to Lambda you don’t need to run a server just for handling a few form submissions and use a static site generator for your website.

ResourceLink
Github repository of this tutorialgithub.com/storyblok/lambda-form-submission
Amazon AWS consoleconsole.aws.amazon.com
Storyblok AppStoryblok