{"id":46135,"date":"2018-05-04T04:19:19","date_gmt":"2018-05-04T11:19:19","guid":{"rendered":"http:\/\/www.taterboy.com\/blog\/2018\/05\/building-a-serverless-contact-form-for-your-static-site\/"},"modified":"2018-05-04T04:19:19","modified_gmt":"2018-05-04T11:19:19","slug":"building-a-serverless-contact-form-for-your-static-site","status":"publish","type":"post","link":"https:\/\/www.taterboy.com\/blog\/2018\/05\/building-a-serverless-contact-form-for-your-static-site\/","title":{"rendered":"Building A Serverless Contact Form For Your Static Site"},"content":{"rendered":"<link rel=\"canonical\" href=\"https:\/\/www.smashingmagazine.com\/2018\/05\/building-serverless-contact-form-static-website\/\"><title>Building A Serverless Contact Form For Your Static Site<\/title><\/p>\n<article>\n<header>\n<h1>Building A Serverless Contact Form For Your Static Site<\/h1>\n<address>Brian Holt<\/address>\n<p>                  <time datetime=\"2018-05-02T18:30:17+02:00\">2018-05-02T18:30:17+02:00<\/time><time datetime=\"2018-05-04T09:06:01+00:00\">2018-05-04T09:06:01+00:00<\/time><\/header>\n<p>Static site generators provide a fast and simple alternative to Content Management Systems (CMS) like <a href=\"https:\/\/wordpress.com\/\">WordPress<\/a>. There\u2019s no server or database setup, just a build process and simple HTML, CSS, and JavaScript. Unfortunately, without a server, it\u2019s easy to hit their limits quickly. For instance, in adding a contact form.<\/p>\n<p>With the rise of <a href=\"https:\/\/martinfowler.com\/articles\/serverless.html\">serverless architecture<\/a> adding a contact form to your static site doesn\u2019t need to be the reason to switch to a CMS anymore. It\u2019s possible to get the best of both worlds: a static site with a serverless back-end for the contact form (that you don\u2019t need to maintain). Maybe best of all, in low-traffic sites, like portfolios, the high limits of many serverless providers make these services completely free!<\/p>\n<p>In this article, you\u2019ll learn the basics of Amazon Web Services (AWS) <a href=\"https:\/\/aws.amazon.com\/lambda\/\">Lambda<\/a> and <a href=\"https:\/\/aws.amazon.com\/ses\/\">Simple Email Service (SES)<\/a> APIs to build your own static site mailer on the <a href=\"https:\/\/serverless.com\/framework\/\">Serverless Framework<\/a>. The full service will take form data submitted from an AJAX request, hit the Lambda endpoint, parse the data to build the SES parameters, send the email address, and return a response for our users. I\u2019ll guide you through getting Serverless set up for the first time through deployment. It should take under an hour to complete, so let\u2019s get started!<\/p>\n<figure><a href=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/form-submission.gif\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/form-submission.gif?resize=606%2C532\" width=\"606\" height=\"532\" alt=\"The static site form, sending the message to the Lambda endpoint and returning a response to the user.\"><\/a><figcaption>The static site form, sending the message to the Lambda endpoint and returning a response to the user.<\/figcaption><\/figure>\n<aside data-audience=\"non-subscriber\">\n<div>\n<div>\n<p>Nope, we can&#8217;t do any magic tricks, but we have articles, <a href=\"https:\/\/www.smashingmagazine.com\/books\">books<\/a> and <a href=\"https:\/\/www.smashingmagazine.com\/smashing-tv\/\">webinars<\/a> featuring techniques we all can use to improve our work. <a href=\"https:\/\/www.smashingmagazine.com\/membership\/?utm_medium=panel\">Smashing Members<\/a> get a seasoned selection of magic front-end tricks \u2014 e.g. <strong>live designing sessions<\/strong> and perf audits, too. <em>Just sayin&#8217;<\/em>! ;-)<\/p>\n<p>      <a href=\"https:\/\/www.smashingmagazine.com\/membership\/?utm_medium=panel\"><br \/>\n        Explore Smashing Wizardry\u00a0?<br \/>\n      <\/a>\n      <\/div>\n<div>\n        <a href=\"https:\/\/www.smashingmagazine.com\/membership\/?utm_medium=panel\"><\/p>\n<div>\n          <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.smashingmagazine.com\/images\/smashing-cat\/cat-wizard.svg\" alt=\"Smashing Cat, just preparing to do some magic stuff.\" width=\"310\" height=\"400\"><\/div>\n<p>      <\/a>\n      <\/div>\n<\/p><\/div>\n<\/aside>\n<div>\n<h3>Setting Up<\/h3>\n<p>There are minimal prerequisites in getting started with Serverless technology. For our purposes, it\u2019s simply a <a href=\"https:\/\/nodejs.org\/en\/\">Node Environment<\/a> with <a href=\"https:\/\/yarnpkg.com\/lang\/en\/\">Yarn<\/a>, the <a href=\"https:\/\/serverless.com\/framework\/\">Serverless Framework<\/a>, and an <a href=\"https:\/\/aws.amazon.com\/s\/dm\/optimization\/server-side-test\/free-tier\/free_np\/\">AWS account<\/a>.<\/p>\n<h4>Setting Up The Project<\/h4>\n<figure><a href=\"https:\/\/i0.wp.com\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/38189e1a-bf20-464b-8132-bf8f6e005bfe\/serverless-framework.png?ssl=1\"><br \/>\n\t\t<img data-recalc-dims=\"1\" decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/serverless-framework.png 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/38189e1a-bf20-464b-8132-bf8f6e005bfe\/serverless-framework.png 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/38189e1a-bf20-464b-8132-bf8f6e005bfe\/serverless-framework.png 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/38189e1a-bf20-464b-8132-bf8f6e005bfe\/serverless-framework.png 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/38189e1a-bf20-464b-8132-bf8f6e005bfe\/serverless-framework.png 2000w\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/serverless-framework.png?w=900\" sizes=\"100vw\" alt=\"The Serverless Framework web site. Useful for installation and documentation.\"><\/a><figcaption>\n\t\t\tThe Serverless Framework web site. Useful for installation and documentation.<br \/>\n\t\t<\/figcaption><\/figure>\n<p>We use Yarn to install the Serverless Framework to a local directory.<\/p>\n<ol>\n<li>Create a new directory to host the project.<\/li>\n<li>Navigate to the directory in your command line interface.<\/li>\n<li>Run <code>yarn init<\/code> to create a <code>package.json<\/code> file for this project.<\/li>\n<li>Run <code>yarn add serverless<\/code> to install the framework locally.<\/li>\n<li>Run <code>yarn serverless create --template aws-nodejs --name static-site-mailer<\/code> to create a Node service template and name it <code>static-site-mailer<\/code>.<\/li>\n<\/ol>\n<p>Our project is setup but we won\u2019t be able to do anything until we set up our AWS services.<\/p>\n<h4>Setting Up An Amazon Web Services Account, Credentials, And Simple Email Service<\/h4>\n<figure><a href=\"https:\/\/i0.wp.com\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/15c1d472-387a-4db1-ac30-236dd30413b8\/aws-page.png?ssl=1\"><br \/>\n\t\t<img data-recalc-dims=\"1\" decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/aws-page.png 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/15c1d472-387a-4db1-ac30-236dd30413b8\/aws-page.png 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/15c1d472-387a-4db1-ac30-236dd30413b8\/aws-page.png 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/15c1d472-387a-4db1-ac30-236dd30413b8\/aws-page.png 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/15c1d472-387a-4db1-ac30-236dd30413b8\/aws-page.png 2000w\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/aws-page.png?w=900\" sizes=\"100vw\" alt=\"The Amazon Web Services sign up page, which includes a generous free tier, enabling our project to be entirely free.\"><\/a><figcaption>\n\t\t\tThe Amazon Web Services sign up page, which includes a generous free tier, enabling our project to be entirely free.<br \/>\n\t\t<\/figcaption><\/figure>\n<p>The Serverless Framework has recorded a <a href=\"https:\/\/www.youtube.com\/watch?v=KngM5bfpttA\">video walk-through<\/a> for setting up AWS credentials, but I\u2019ve listed the steps here as well.<\/p>\n<ol>\n<li>Sign Up for an <a href=\"https:\/\/aws.amazon.com\/s\/dm\/optimization\/server-side-test\/free-tier\/free_np\/\">AWS account<\/a> or log in if you already have one.<\/li>\n<li>In the AWS search bar, search for \u201cIAM\u201d.<\/li>\n<li>On the IAM page, click on \u201cUsers\u201d on the sidebar, then the \u201cAdd user\u201d button.<\/li>\n<li>On the Add user page, give the user a name \u2013 something like \u201cserverless\u201d is appropriate. Check \u201cProgrammatic access\u201d under Access type then click next.<\/li>\n<li>On the permissions screen, click on the \u201cAttach existing policies directly\u201d tab, search for \u201cAdministratorAccess\u201d in the list, check it, and click next.<\/li>\n<li>On the review screen you should see your user name, with \u201cProgrammatic access\u201d, and \u201cAdministratorAccess\u201d, then create the user.<\/li>\n<li>The confirmation screen shows the user \u201cAccess key ID\u201d and \u201cSecret access key\u201d, you\u2019ll need these to provide the Serverless Framework with access. In your CLI, type <code>yarn sls config credentials --provider aws --key YOUR_ACCESS_KEY_ID --secret YOUR_SECRET_ACCESS_KEY<\/code>, replacing <code>YOUR_ACCESS_KEY_ID<\/code> and <code>YOUR_SECRET_ACCESS_KEY<\/code> with the keys on the confirmation screen.<\/li>\n<\/ol>\n<p>Your credentials are configured now, but while we\u2019re in the AWS console let\u2019s set up Simple Email Service.<\/p>\n<ol>\n<li>Click Console Home in the top left corner to go home.<\/li>\n<li>On the home page, in the AWS search bar, search for \u201cSimple Email Service\u201d.<\/li>\n<li>On the SES Home page, click on \u201cEmail Addresses\u201d in the sidebar.<\/li>\n<li>On the Email Addresses listing page, click the \u201cVerify a New Email Address\u201d button.<\/li>\n<li>In the dialog window, type your email address then click \u201cVerify This Email Address\u201d.<\/li>\n<li>You\u2019ll receive an email in moments containing a link to verify the address. Click on the link to complete the process.<\/li>\n<\/ol>\n<p>Now that our accounts are made, let\u2019s take a peek at the Serverless template files.<\/p>\n<h4>Setting Up The Serverless Framework<\/h4>\n<div data-impression=\"true\" data-audience=\"non-subscriber\" data-remove=\"true\"><\/div>\n<p>Running <code>serverless create<\/code> creates two files: <a href=\"http:\/\/provide.smashingmagazine.com\/handler.js\">handler.js<\/a> which contains the Lambda function, and <a href=\"http:\/\/provide.smashingmagazine.com\/serverless.yml\">serverless.yml<\/a> which is the configuration file for the entire Serverless Architecture. Within the configuration file, you can specify as many handlers as you\u2019d like, and each one will map to a new function that can interact with other functions. In this project, we\u2019ll only create a single handler, but in a full Serverless Architecture, you\u2019d have several of the various functions of the service.<\/p>\n<figure><a href=\"https:\/\/i0.wp.com\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/99127c53-2dbd-4b76-b432-7a8ac8003434\/sls-file-structure.png?ssl=1\"><br \/>\n\t\t<img data-recalc-dims=\"1\" decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/sls-file-structure.png 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/99127c53-2dbd-4b76-b432-7a8ac8003434\/sls-file-structure.png 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/99127c53-2dbd-4b76-b432-7a8ac8003434\/sls-file-structure.png 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/99127c53-2dbd-4b76-b432-7a8ac8003434\/sls-file-structure.png 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/99127c53-2dbd-4b76-b432-7a8ac8003434\/sls-file-structure.png 2000w\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/sls-file-structure.png?w=900\" sizes=\"100vw\" alt=\"The default file structure generated from the Serverless Framework containing handler.js and serverless.yml.\"><\/a><figcaption>\n\t\t\tThe default file structure generated from the Serverless Framework containing <a href=\"http:\/\/provide.smashingmagazine.com\/handler.js\">handler.js<\/a> and <a href=\"http:\/\/provide.smashingmagazine.com\/serverless.yml\">serverless.yml<\/a>.<br \/>\n\t\t<\/figcaption><\/figure>\n<p>In <a href=\"http:\/\/provide.smashingmagazine.com\/handler.js\">handler.js<\/a>, you\u2019ll see a single exported function named <code>hello<\/code>. This is currently the main (and only) function. It, along with all Node handlers, take three parameters:<\/p>\n<ul>\n<li><code>event<\/code><br \/>\nThis can be thought of as the input data for the function.<\/li>\n<li><a href=\"https:\/\/docs.aws.amazon.com\/lambda\/latest\/dg\/nodejs-prog-model-context.html\"><code>context object<\/code><\/a><br \/>\nThis contains the runtime information of the Lambda function.<\/li>\n<li><a href=\"https:\/\/docs.aws.amazon.com\/lambda\/latest\/dg\/nodejs-prog-model-handler.html#nodejs-prog-model-handler-callback\"><code>callback<\/code><\/a><br \/>\nAn optional parameter to return information to the caller.<\/li>\n<\/ul>\n<pre><code>\/\/ handler.js\n\n'use strict';\n\nmodule.exports.hello = (event, context, callback) => {\n  const response = {\n    statusCode: 200,\n    body: JSON.stringify({\n      message: 'Go Serverless v1.0! Your function executed successfully!',\n      input: event,\n    }),\n  };\n\n  callback(null, response);\n};\n<\/code><\/pre>\n<p>At the bottom of <code>hello<\/code>, there\u2019s a callback. It\u2019s an optional argument to return a response, but if it\u2019s not <em>explicitly<\/em> called, it will <em>implicitly<\/em> return with <code>null<\/code>. The callback takes <a href=\"https:\/\/docs.aws.amazon.com\/lambda\/latest\/dg\/nodejs-prog-model-handler.html#nodejs-prog-model-handler-callback\">two parameters<\/a>:<\/p>\n<ul>\n<li><strong>Error error<\/strong><br \/>\nFor providing error information for when the Lambda itself fails. When the Lambda succeeds, <code>null<\/code> should be passed into this parameter.<\/li>\n<li><strong>Object result<\/strong><br \/>\nFor providing a response object. It must be <code>JSON.stringify<\/code> compatible. If there\u2019s a parameter in the error field, this field is ignored.<\/li>\n<\/ul>\n<p>Our static site will send our form data in the event body and the callback will return a response for our user to see.<\/p>\n<p>In <a href=\"http:\/\/provide.smashingmagazine.com\/serverless.yml\">serverless.yml<\/a> you\u2019ll see the name of the service, provider information, and the functions.<\/p>\n<pre><code># serverless.yml\n\nservice: static-site-mailer\n\nprovider:\n  name: aws\n  runtime: nodejs6.10\n\nfunctions:\n  hello:\n    handler: handler.hello\n<\/code><\/pre>\n<figure><a href=\"https:\/\/i0.wp.com\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/6677f489-dbd3-4a6a-b7c5-ff6237a57474\/mapping.png?ssl=1\"><br \/>\n\t\t<img data-recalc-dims=\"1\" decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/mapping.png 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/6677f489-dbd3-4a6a-b7c5-ff6237a57474\/mapping.png 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/6677f489-dbd3-4a6a-b7c5-ff6237a57474\/mapping.png 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/6677f489-dbd3-4a6a-b7c5-ff6237a57474\/mapping.png 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/6677f489-dbd3-4a6a-b7c5-ff6237a57474\/mapping.png 2000w\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/mapping.png?w=900\" sizes=\"100vw\" alt=\"How the function names in serverless.yml map to handler.js.\"><\/a><figcaption>\n\t\t\tHow the function names in <a href=\"http:\/\/provide.smashingmagazine.com\/serverless.yml\">serverless.yml<\/a> map to <a href=\"http:\/\/provide.smashingmagazine.com\/handler.js\">handler.js<\/a>.<br \/>\n\t\t<\/figcaption><\/figure>\n<p>Notice the mapping between the hello function and the handler? We can name our file and function anything and as long as it maps to the configuration it will work. Let\u2019s rename our function to <code>staticSiteMailer<\/code>.<\/p>\n<pre><code># serverless.yml\n\nfunctions:\n  staticSiteMailer:\n    handler: handler.staticSiteMailer\n<\/code><\/pre>\n<pre><code>\/\/ handler.js\n\nmodule.exports.staticSiteMailer = (event, context, callback) => {\n  ...\n};\n<\/code><\/pre>\n<p>Lambda functions need permission to interact with other AWS infrastructure. Before we can send an email, we need to allow SES to do so. In <a href=\"http:\/\/provide.smashingmagazine.com\/serverless.yml\">serverless.yml<\/a>, under <code>provider.iamRoleStatements<\/code> add the permission.<\/p>\n<pre><code># serverless.yml\n\nprovider:\n  name: aws\n  runtime: nodejs6.10\n  iamRoleStatements:\n    - Effect: \"Allow\"\n      Action:\n        - \"ses:SendEmail\"\n      Resource: [\"*\"]\n<\/code><\/pre>\n<p>Since we need a URL for our form action, we need to add HTTP events to our function. In <a href=\"http:\/\/provide.smashingmagazine.com\/serverless.yml\">serverless.yml<\/a> we create a path, specify the method as <code>post<\/code>, and set CORS to true for security.<\/p>\n<pre><code>functions:\n  staticSiteMailer:\n    handler: handler.staticSiteMailer\n    events:\n      - http:\n          method: post\n          path: static-site-mailer\n          cors: true\n<\/code><\/pre>\n<p>Our updated <a href=\"http:\/\/provide.smashingmagazine.com\/serverless.yml\">serverless.yml<\/a> and <a href=\"http:\/\/provide.smashingmagazine.com\/handler.js\">handler.js<\/a> files should look like:<\/p>\n<pre><code># serverless.yml\n\nservice: static-site-mailer\n\nprovider:\n  name: aws\n  runtime: nodejs6.10\n\nfunctions:\n  staticSiteMailer:\n    handler: handler.staticSiteMailer\n    events:\n      - http:\n          method: post\n          path: static-site-mailer\n          cors: true\n\nprovider:\n  name: aws\n  runtime: nodejs6.10\n  iamRoleStatements:\n    - Effect: \"Allow\"\n      Action:\n        - \"ses:SendEmail\"\n      Resource: [\"*\"]\n<\/code><\/pre>\n<pre><code>\/\/ handler.js\n\n'use strict';\n\nmodule.exports.staticSiteMailer = (event, context, callback) => {\n  const response = {\n    statusCode: 200,\n    body: JSON.stringify({\n      message: 'Go Serverless v1.0! Your function executed successfully!',\n      input: event,\n    }),\n  };\n\n  callback(null, response);\n};\n<\/code><\/pre>\n<p>Our Serverless Architecture is setup, so let\u2019s deploy it and test it. You\u2019ll get a simple JSON response.<\/p>\n<pre><code>yarn sls deploy --verbose\nyarn sls invoke --function staticSiteMailer\n\n{\n    \"statusCode\": 200,\n    \"body\": \"{\"message\":\"Go Serverless v1.0! Your function executed successfully!\",\"input\":{}}\"\n}\n<\/code><\/pre>\n<figure><a href=\"https:\/\/i0.wp.com\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/6e655ec6-3562-4834-922c-bc1c1004dac5\/response.png?ssl=1\"><br \/>\n\t\t<img data-recalc-dims=\"1\" decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/response.png 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/6e655ec6-3562-4834-922c-bc1c1004dac5\/response.png 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/6e655ec6-3562-4834-922c-bc1c1004dac5\/response.png 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/6e655ec6-3562-4834-922c-bc1c1004dac5\/response.png 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/6e655ec6-3562-4834-922c-bc1c1004dac5\/response.png 2000w\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/response.png?w=900\" sizes=\"100vw\" alt=\"The return response from invoking our brand new serverless function.\"><\/a><figcaption>\n\t\t\tThe return response from invoking our brand new serverless function.<br \/>\n\t\t<\/figcaption><\/figure>\n<h3>Creating The HTML Form<\/h3>\n<p>Our Lambda function input and form output need to match, so before we build the function we\u2019ll build the form and capture its output. We keep it simple with name, email, and message fields. We\u2019ll add the form action once we\u2019ve deployed our serverless architecture and got our URL, but we know it will be a POST request so we can add that in. At the end of the form, we add a paragraph tag for displaying response messages to the user which we\u2019ll update on the submission callback.<\/p>\n<pre><code><form action=\"{{ SERVICE URL }}\" method=\"POST\">\n  <label>\n    Name\n    <input type=\"text\" name=\"name\" required>\n  <\/label>\n  <label>\n    Email\n    <input type=\"email\" name=\"reply_to\" required>\n  <\/label>\n  <label>\n    Message:\n    <textarea name=\"message\" required><\/textarea>\n  <\/label>\n  <button type=\"submit\">Send Message<\/button>\n<\/form>\n<p id=\"js-form-response\"><\/p>\n<\/code><\/pre>\n<p>To capture the output we add a submit handler to the form, turn our form parameters into an object, and send stringified JSON to our Lambda function. In the Lambda function we use <code>JSON.parse()<\/code> to read our data. Alternatively, you could use <a href=\"https:\/\/api.jquery.com\/serialize\/\">jQuery\u2019s Serialize<\/a> or <a href=\"https:\/\/www.npmjs.com\/package\/query-string\">query-string<\/a> to send and parse the form parameters as a query string but <code>JSON.stringify()<\/code> and <code>JSON.parse()<\/code> are native.<\/p>\n<pre><code>(() => {\n  const form = document.querySelector('form');\n  const formResponse = document.querySelector('js-form-response');\n\n  form.onsubmit = e => {\n    e.preventDefault();\n\n    \/\/ Prepare data to send\n    const data = {};\n    const formElements = Array.from(form);\n    formElements.map(input => (data[input.name] = input.value));\n\n    \/\/ Log what our lambda function will receive\n    console.log(JSON.stringify(data));\n  };\n})();\n<\/code><\/pre>\n<p>Go ahead and submit your form then capture the console output. We\u2019ll use it in our Lambda function next.<\/p>\n<figure><a href=\"https:\/\/i0.wp.com\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/5c14cf58-3540-4ae2-9304-585ffe87991b\/form-submission-capture.png?ssl=1\"><br \/>\n\t\t<img data-recalc-dims=\"1\" decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/form-submission-capture.png 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/5c14cf58-3540-4ae2-9304-585ffe87991b\/form-submission-capture.png 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/5c14cf58-3540-4ae2-9304-585ffe87991b\/form-submission-capture.png 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/5c14cf58-3540-4ae2-9304-585ffe87991b\/form-submission-capture.png 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/5c14cf58-3540-4ae2-9304-585ffe87991b\/form-submission-capture.png 2000w\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/form-submission-capture.png?w=900\" sizes=\"100vw\" alt=\"Capturing the form data in a console log.\"><\/a><figcaption>\n\t\t\tCapturing the form data in a console log.<br \/>\n\t\t<\/figcaption><\/figure>\n<h3>Invoking Lambda Functions<\/h3>\n<p>Especially during development, we need to test our function does what we expect. The Serverless Framework provides the <a href=\"https:\/\/serverless.com\/framework\/docs\/providers\/aws\/cli-reference\/invoke\/\"><code>invoke<\/code><\/a> and <a href=\"https:\/\/serverless.com\/framework\/docs\/providers\/aws\/cli-reference\/invoke-local\/\"><code>invoke local<\/code><\/a> command to trigger your function from <em>live<\/em> and <em>development<\/em> environments respectively. Both commands require the function name passed through, in our case <code>staticSiteMailer<\/code>.<\/p>\n<pre><code>yarn sls invoke local --function staticSiteMailer\n<\/code><\/pre>\n<p>To pass mock data into our function, create a new file named <code>data.json<\/code> with the captured console output under a <code>body<\/code> key within a JSON object. It should look something like:<\/p>\n<pre><code>\/\/ data.json\n\n{\n  \"body\": \"{\"name\": \"Sender Name\",\"reply_to\": \"sender@email.com\",\"message\": \"Sender message\"}\"\n}\n<\/code><\/pre>\n<p>To invoke the function with the local data, pass the <code>--path<\/code> argument along with the path to the file.<\/p>\n<pre><code>yarn sls invoke local --function staticSiteMailer --path data.json\n<\/code><\/pre>\n<figure><a href=\"https:\/\/i0.wp.com\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/83d98e0e-fe10-4a81-9837-dbbb45ac198f\/response-2.png?ssl=1\"><br \/>\n\t\t<img data-recalc-dims=\"1\" decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/response-2.png 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/83d98e0e-fe10-4a81-9837-dbbb45ac198f\/response-2.png 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/83d98e0e-fe10-4a81-9837-dbbb45ac198f\/response-2.png 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/83d98e0e-fe10-4a81-9837-dbbb45ac198f\/response-2.png 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/83d98e0e-fe10-4a81-9837-dbbb45ac198f\/response-2.png 2000w\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/response-2.png?w=900\" sizes=\"100vw\" alt=\"An updated return response from our serverless function when we pass it JSON data.\"><\/a><figcaption>\n\t\t\tAn updated return response from our serverless function when we pass it JSON data.<br \/>\n\t\t<\/figcaption><\/figure>\n<p>You\u2019ll see a similar response to before, but the <code>input<\/code> key will contain the event we mocked. Let\u2019s use our mock data to send an email using Simple Email Service!<\/p>\n<h3>Sending An Email With Simple Email Service<\/h3>\n<p>We\u2019re going to replace the <code>staticSiteMailer<\/code> function with a call to a private <code>sendEmail<\/code> function. For now you can comment out or remove the template code and replace it with:<\/p>\n<pre><code>\/\/ hander.js\n\nfunction sendEmail(formData, callback) {\n  \/\/ Build the SES parameters\n  \/\/ Send the email\n}\n\nmodule.exports.staticSiteMailer = (event, context, callback) => {\n  const formData = JSON.parse(event.body);\n\n  sendEmail(formData, function(err, data) {\n    if (err) {\n      console.log(err, err.stack);\n    } else {\n      console.log(data);\n    }\n  });\n};\n<\/code><\/pre>\n<p>First, we parse the <code>event.body<\/code> to capture the form data, then we pass it to a private <code>sendEmail<\/code> function. <code>sendEmail<\/code> is responsible for sending the email, and the callback function will return a failure or success response with <code>err<\/code> or <code>data<\/code>. In our case, we can simply log the error or data since we\u2019ll be replacing this with the Lambda callback in a moment.<\/p>\n<p>Amazon provides a convenient SDK, <code>aws-sdk<\/code>, for connecting their services with Lambda functions. Many of their services, including SES, are part of it. We add it to the project with <code>yarn add aws-sdk<\/code> and import it into the top the handler file.<\/p>\n<pre><code>\/\/ handler.js\n\nconst AWS = require('aws-sdk');\nconst SES = new AWS.SES();\n<\/code><\/pre>\n<p>In our private <code>sendEmail<\/code> function, we build the <a href=\"https:\/\/docs.aws.amazon.com\/AWSJavaScriptSDK\/latest\/AWS\/SES.html#sendEmail-property\"><code>SES.sendEmail<\/code> parameters<\/a> from the parsed form data and use the callback to return a response to the caller. The parameters require the following as an object:<\/p>\n<ul>\n<li><strong>Source<\/strong><br \/>\nThe email address SES is sending <em>from<\/em>.<\/li>\n<li><strong>ReplyToAddresses<\/strong><br \/>\nAn array of email addresses added to the reply to the field in the email.<\/li>\n<li><strong>Destination<\/strong><br \/>\nAn object that must contain at least one <strong>ToAddresses<\/strong>, <strong>CcAddresses<\/strong>, or <strong>BccAddresses<\/strong>. Each field takes an array of email addresses that correspond to the <em>to<\/em>, <em>cc<\/em>, and <em>bcc<\/em> fields respectively.<\/li>\n<li><strong>Message<\/strong><br \/>\nAn object which contains the <strong>Body<\/strong> and <strong>Subject<\/strong>.<\/li>\n<\/ul>\n<aside data-audience=\"non-subscriber\" data-remove=\"true\">\n<div>\n<div>\n<p>\n          Is your pattern library up to date today? <em>Alla Kholmatova<\/em> has just finished a fully fledged book on <strong>Design Systems<\/strong> and how to get them right. With common traps, gotchas and the lessons she learned. Hardcover, eBook. <em>Just sayin&#8217;.<\/em>\n        <\/p>\n<p>        <a href=\"https:\/\/www.smashingmagazine.com\/printed-books\/design-systems\/\"><br \/>\n          <button><br \/>\n            Table of Contents\u00a0?<br \/>\n          <\/button><br \/>\n        <\/a>\n      <\/div>\n<div>\n        <a href=\"https:\/\/www.smashingmagazine.com\/printed-books\/design-systems\/\"><\/p>\n<div>\n          <img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/design-systems-large-opt.png?w=900\"><\/div>\n<p>      <\/a>\n      <\/div>\n<\/p><\/div>\n<\/aside>\n<p>Since <code>formData<\/code> is an object we can call our form fields directly like <code>formData.message<\/code>, build our parameters, and send it. We pass <em>your<\/em> SES-verified email to <code>Source<\/code> and <code>Destination.ToAddresses<\/code>. As long as the email is verified you can pass anything here, including different email addresses. We pluck our <code>reply_to<\/code>, <code>message<\/code>, and <code>name<\/code> off our <code>formData<\/code> object to fill in the <code>ReplyToAddresses<\/code> and <code>Message.Body.Text.Data<\/code> fields.<\/p>\n<pre><code>\/\/ handler.js\nfunction sendEmail(formData, callback) {\n  const emailParams = {\n    Source: 'your_email@example.com', \/\/ SES SENDING EMAIL\n    ReplyToAddresses: [formData.reply_to],\n    Destination: {\n      ToAddresses: ['your_email@example.com'], \/\/ SES RECEIVING EMAIL\n    },\n    Message: {\n      Body: {\n        Text: {\n          Charset: 'UTF-8',\n          Data: `${formData.message}nnName: ${formData.name}nEmail: ${formData.reply_to}`,\n        },\n      },\n      Subject: {\n        Charset: 'UTF-8',\n        Data: 'New message from your_site.com',\n      },\n    },\n  };\n\n  SES.sendEmail(emailParams, callback);\n}\n<\/code><\/pre>\n<p><code>SES.sendEmail<\/code> will send the email and our callback will return a response. Invoking the local function will send an email to your verified address.<\/p>\n<pre><code>yarn sls invoke local --function testMailer --path data.json\n<\/code><\/pre>\n<figure><a href=\"https:\/\/i0.wp.com\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/3cf13126-7715-4e9d-9920-b6264b98e70c\/response-3.png?ssl=1\"><br \/>\n\t\t<img data-recalc-dims=\"1\" decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/response-3.png 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/3cf13126-7715-4e9d-9920-b6264b98e70c\/response-3.png 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/3cf13126-7715-4e9d-9920-b6264b98e70c\/response-3.png 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/3cf13126-7715-4e9d-9920-b6264b98e70c\/response-3.png 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/3cf13126-7715-4e9d-9920-b6264b98e70c\/response-3.png 2000w\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/response-3.png?w=900\" sizes=\"100vw\" alt=\"The return response from SES.sendEmail when it succeeds.\"><\/a><figcaption>\n\t\t\tThe return response from <code>SES.sendEmail<\/code> when it succeeds.<br \/>\n\t\t<\/figcaption><\/figure>\n<h3>Returning A Response From The Handler<\/h3>\n<p>Our function sends an email using the command line, but that\u2019s not how our users will interact with it. We need to return a response to our AJAX form submission. If it fails, we should return an appropriate <code>statusCode<\/code> as well as the <code>err.message<\/code>. When it succeeds, the <code>200<\/code> <code>statusCode<\/code> is sufficient, but we\u2019ll return the mailer response in the body as well. In <code>staticSiteMailer<\/code> we build our response data and replace our <code>sendEmail<\/code> callback function with the Lambda callback.<\/p>\n<pre><code>\/\/ handler.js\n\nmodule.exports.staticSiteMailer = (event, context, callback) => {\n  const formData = JSON.parse(event.body);\n\n  sendEmail(formData, function(err, data) {\n    const response = {\n      statusCode: err ? 500 : 200,\n      headers: {\n        'Content-Type': 'application\/json',\n        'Access-Control-Allow-Origin': 'https:\/\/your-domain.com',\n      },\n      body: JSON.stringify({\n        message: err ? err.message : data,\n      }),\n    };\n\n    callback(null, response);\n  });\n};\n<\/code><\/pre>\n<p>Our Lambda callback now returns both success and failure messages from <code>SES.sendEmail<\/code>. We build the response with checks if <code>err<\/code> is present so our response is consistent. The Lambda callback function itself passes <code>null<\/code> in the error argument field and the response as the second. We want to pass errors onwards, but if the Lambda itself fails, its <a href=\"https:\/\/docs.aws.amazon.com\/lambda\/latest\/dg\/nodejs-prog-model-handler.html#nodejs-prog-model-handler-callback\">callback will be implicitly called<\/a> with the error response.<\/p>\n<p>In the <code>headers<\/code>, you\u2019ll need to replace <code>Access-Control-Allow-Origin<\/code> with your own domain. This will prevent any other domains from using your service and potentially racking up an AWS bill in your name! And I don\u2019t cover it in this article, but it\u2019s possible to set-up Lambda to use your own domain. You\u2019ll need to have an SSL\/TLS certificate uploaded to Amazon. The Serverless Framework team wrote a <a href=\"https:\/\/serverless.com\/blog\/serverless-api-gateway-domain\/\">fantastic tutorial<\/a> on how to do so.<\/p>\n<p>Invoking the local function will now send an email and return the appropriate response.<\/p>\n<pre><code>yarn sls invoke local --function testMailer --path data.json\n<\/code><\/pre>\n<figure><a href=\"https:\/\/i0.wp.com\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/8001f309-c77d-4a1d-af59-f6d0bbb958ac\/response-4.png?ssl=1\"><br \/>\n\t\t<img data-recalc-dims=\"1\" decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/response-4.png 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/8001f309-c77d-4a1d-af59-f6d0bbb958ac\/response-4.png 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/8001f309-c77d-4a1d-af59-f6d0bbb958ac\/response-4.png 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/8001f309-c77d-4a1d-af59-f6d0bbb958ac\/response-4.png 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/8001f309-c77d-4a1d-af59-f6d0bbb958ac\/response-4.png 2000w\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/response-4.png?w=900\" sizes=\"100vw\" alt=\"The return response from our serverless function, containing the SES.sendEmail return response in the body.\"><\/a><figcaption>\n\t\t\tThe return response from our serverless function, containing the SES.sendEmail return response in the body.<br \/>\n\t\t<\/figcaption><\/figure>\n<h3>Calling The Lambda Function From The Form<\/h3>\n<p>Our service is complete! To deploy it run <code>yarn sls deploy -v<\/code>. Once it\u2019s deployed you\u2019ll get a URL that looks something like <code>https:\/\/r4nd0mh45h.execute-api.us-east-1.amazonaws.com\/dev\/static-site-mailer<\/code> which you can add to the form action. Next, we create the AJAX request and return the response to the user.<\/p>\n<pre><code>(() => {\n  const form = document.querySelector('form');\n  const formResponse = document.querySelector('js-form-response');\n\n  form.onsubmit = e => {\n    e.preventDefault();\n\n    \/\/ Prepare data to send\n    const data = {};\n    const formElements = Array.from(form);\n    formElements.map(input => (data[input.name] = input.value));\n\n    \/\/ Log what our lambda function will receive\n    console.log(JSON.stringify(data));\n\n    \/\/ Construct an HTTP request\n    var xhr = new XMLHttpRequest();\n    xhr.open(form.method, form.action, true);\n    xhr.setRequestHeader('Accept', 'application\/json; charset=utf-8');\n    xhr.setRequestHeader('Content-Type', 'application\/json; charset=UTF-8');\n\n    \/\/ Send the collected data as JSON\n    xhr.send(JSON.stringify(data));\n\n    \/\/ Callback function\n    xhr.onloadend = response => {\n      if (response.target.status === 200) {\n        \/\/ The form submission was successful\n        form.reset();\n        formResponse.innerHTML = 'Thanks for the message. I\u2019ll be in touch shortly.';\n      } else {\n        \/\/ The form submission failed\n        formResponse.innerHTML = 'Something went wrong';\n        console.error(JSON.parse(response.target.response).message);\n      }\n    };\n  };\n})();\n<\/code><\/pre>\n<p>In the AJAX callback, we check the status code with <code>response.target.status<\/code>. If it\u2019s anything other than <code>200<\/code> we can show an error message to the user, otherwise let them know the message was sent. Since our Lambda returns stringified JSON we can parse the body message with <code>JSON.parse(response.target.response).message<\/code>. It\u2019s especially useful to log the error.<\/p>\n<p>You should be able to submit your form entirely from your static site!<\/p>\n<figure><a href=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/form-submission.gif\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/form-submission.gif?resize=606%2C532\" width=\"606\" height=\"532\" alt=\"The static site form, sending the message to the Lambda endpoint and returning a response to the user.\"><\/a><figcaption>The static site form, sending the message to the Lambda endpoint and returning a response to the user.<\/figcaption><\/figure>\n<h3>Next Steps<\/h3>\n<p>Adding a contact form to your static is easy with the Serverless Framework and AWS. There\u2019s room for improvement in our code, like adding <a href=\"http:\/\/jennamolby.com\/how-to-prevent-form-spam-by-using-the-honeypot-technique\/\">form validation with a honeypot<\/a>, preventing AJAX calls for invalid forms and improving the UX if the response, but this is enough to get started. You can see some of these improvements within the <a href=\"https:\/\/github.com\/bholtbholt\/static-site-mailer\">static site mailer repo<\/a> I\u2019ve created. I hope I\u2019ve inspired you to try out Serverless yourself!<\/p>\n<div>\n  <img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/logo-red.png?w=900\" alt=\"Smashing Editorial\"><span>(lf, ra, il)<\/span>\n<\/div>\n<\/div>\n<div data-impression=\"true\" data-audience=\"non-subscriber\" data-remove=\"true\"><\/div>\n<\/article>\n<p class=\"wpematico_credit\"><small>Powered by <a href=\"http:\/\/www.wpematico.com\" target=\"_blank\">WPeMatico<\/a><\/small><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Building A Serverless Contact Form For Your Static Site Building A Serverless Contact Form For Your Static Site Brian Holt 2018-05-02T18:30:17+02:002018-05-04T09:06:01+00:00 Static site generators provide a fast and simple alternative to Content Management Systems (CMS) like WordPress. There\u2019s no server or database setup, just a build process and simple HTML, CSS, and JavaScript. Unfortunately, without&#8230;<a class=\"moretag\" href=\"https:\/\/www.taterboy.com\/blog\/2018\/05\/building-a-serverless-contact-form-for-your-static-site\/\"> Read the full article&#8230;<\/a><\/p>\n","protected":false},"author":4,"featured_media":46136,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[371],"tags":[],"class_list":["post-46135","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-user-experience"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.0.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"Building A Serverless Contact Form For Your Static SiteBuilding A Serverless Contact Form For Your Static Site Brian Holt 2018-05-02T18:30:17+02:002018-05-04T09:06:01+00:00Static site generators provide a fast and simple alternative to Content Management Systems (CMS) like WordPress. There\u2019s no server or database setup, just a build process and simple HTML, CSS, and JavaScript. Unfortunately, without a server,\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Guest Contribution\"\/>\n\t<meta name=\"keywords\" content=\"user experience\" \/>\n\t<link rel=\"canonical\" href=\"https:\/\/www.taterboy.com\/blog\/2018\/05\/building-a-serverless-contact-form-for-your-static-site\/\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.0.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"Design for Immersive Technologies | User Experience for Games, Virtual Reality (VR) and Augmented\/Mixed Reality (AR\/MR)\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Building A Serverless Contact Form For Your Static Site | Design for Immersive Technologies\" \/>\n\t\t<meta property=\"og:description\" content=\"Building A Serverless Contact Form For Your Static SiteBuilding A Serverless Contact Form For Your Static Site Brian Holt 2018-05-02T18:30:17+02:002018-05-04T09:06:01+00:00Static site generators provide a fast and simple alternative to Content Management Systems (CMS) like WordPress. There\u2019s no server or database setup, just a build process and simple HTML, CSS, and JavaScript. Unfortunately, without a server,\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/www.taterboy.com\/blog\/2018\/05\/building-a-serverless-contact-form-for-your-static-site\/\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2018-05-04T11:19:19+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2018-05-04T11:19:19+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Building A Serverless Contact Form For Your Static Site | Design for Immersive Technologies\" \/>\n\t\t<meta name=\"twitter:description\" content=\"Building A Serverless Contact Form For Your Static SiteBuilding A Serverless Contact Form For Your Static Site Brian Holt 2018-05-02T18:30:17+02:002018-05-04T09:06:01+00:00Static site generators provide a fast and simple alternative to Content Management Systems (CMS) like WordPress. There\u2019s no server or database setup, just a build process and simple HTML, CSS, and JavaScript. Unfortunately, without a server,\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2018\\\/05\\\/building-a-serverless-contact-form-for-your-static-site\\\/#article\",\"name\":\"Building A Serverless Contact Form For Your Static Site | Design for Immersive Technologies\",\"headline\":\"Building A Serverless Contact Form For Your Static Site\",\"author\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#organization\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/i0.wp.com\\\/www.taterboy.com\\\/blog\\\/wp-content\\\/uploads\\\/2018\\\/05\\\/form-submission.gif?fit=606%2C532&ssl=1\",\"width\":606,\"height\":532},\"datePublished\":\"2018-05-04T04:19:19-07:00\",\"dateModified\":\"2018-05-04T04:19:19-07:00\",\"inLanguage\":\"en-US\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2018\\\/05\\\/building-a-serverless-contact-form-for-your-static-site\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2018\\\/05\\\/building-a-serverless-contact-form-for-your-static-site\\\/#webpage\"},\"articleSection\":\"User Experience\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2018\\\/05\\\/building-a-serverless-contact-form-for-your-static-site\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.taterboy.com\\\/blog\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/category\\\/user-experience\\\/#listItem\",\"name\":\"User Experience\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/category\\\/user-experience\\\/#listItem\",\"position\":2,\"name\":\"User Experience\",\"item\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/category\\\/user-experience\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2018\\\/05\\\/building-a-serverless-contact-form-for-your-static-site\\\/#listItem\",\"name\":\"Building A Serverless Contact Form For Your Static Site\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2018\\\/05\\\/building-a-serverless-contact-form-for-your-static-site\\\/#listItem\",\"position\":3,\"name\":\"Building A Serverless Contact Form For Your Static Site\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/category\\\/user-experience\\\/#listItem\",\"name\":\"User Experience\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#organization\",\"name\":\"Design for Immersive Technologies\",\"description\":\"User Experience for Games, Virtual Reality (VR) and Augmented\\\/Mixed Reality (AR\\\/MR)\",\"url\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/#author\",\"url\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/\",\"name\":\"Guest Contribution\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2018\\\/05\\\/building-a-serverless-contact-form-for-your-static-site\\\/#authorImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/d2aad73eb0f48c67b141e9fc978a0e498969791ed623346e361d02d19775b0bb?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"Guest Contribution\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2018\\\/05\\\/building-a-serverless-contact-form-for-your-static-site\\\/#webpage\",\"url\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2018\\\/05\\\/building-a-serverless-contact-form-for-your-static-site\\\/\",\"name\":\"Building A Serverless Contact Form For Your Static Site | Design for Immersive Technologies\",\"description\":\"Building A Serverless Contact Form For Your Static SiteBuilding A Serverless Contact Form For Your Static Site Brian Holt 2018-05-02T18:30:17+02:002018-05-04T09:06:01+00:00Static site generators provide a fast and simple alternative to Content Management Systems (CMS) like WordPress. There\\u2019s no server or database setup, just a build process and simple HTML, CSS, and JavaScript. Unfortunately, without a server,\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2018\\\/05\\\/building-a-serverless-contact-form-for-your-static-site\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/#author\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/i0.wp.com\\\/www.taterboy.com\\\/blog\\\/wp-content\\\/uploads\\\/2018\\\/05\\\/form-submission.gif?fit=606%2C532&ssl=1\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2018\\\/05\\\/building-a-serverless-contact-form-for-your-static-site\\\/#mainImage\",\"width\":606,\"height\":532},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2018\\\/05\\\/building-a-serverless-contact-form-for-your-static-site\\\/#mainImage\"},\"datePublished\":\"2018-05-04T04:19:19-07:00\",\"dateModified\":\"2018-05-04T04:19:19-07:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/\",\"name\":\"Design for Immersive Technologies\",\"description\":\"User Experience for Games, Virtual Reality (VR) and Augmented\\\/Mixed Reality (AR\\\/MR)\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Building A Serverless Contact Form For Your Static Site | Design for Immersive Technologies","description":"Building A Serverless Contact Form For Your Static SiteBuilding A Serverless Contact Form For Your Static Site Brian Holt 2018-05-02T18:30:17+02:002018-05-04T09:06:01+00:00Static site generators provide a fast and simple alternative to Content Management Systems (CMS) like WordPress. There\u2019s no server or database setup, just a build process and simple HTML, CSS, and JavaScript. Unfortunately, without a server,","canonical_url":"https:\/\/www.taterboy.com\/blog\/2018\/05\/building-a-serverless-contact-form-for-your-static-site\/","robots":"max-image-preview:large","keywords":"user experience","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.taterboy.com\/blog\/2018\/05\/building-a-serverless-contact-form-for-your-static-site\/#article","name":"Building A Serverless Contact Form For Your Static Site | Design for Immersive Technologies","headline":"Building A Serverless Contact Form For Your Static Site","author":{"@id":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/#author"},"publisher":{"@id":"https:\/\/www.taterboy.com\/blog\/#organization"},"image":{"@type":"ImageObject","url":"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/form-submission.gif?fit=606%2C532&ssl=1","width":606,"height":532},"datePublished":"2018-05-04T04:19:19-07:00","dateModified":"2018-05-04T04:19:19-07:00","inLanguage":"en-US","mainEntityOfPage":{"@id":"https:\/\/www.taterboy.com\/blog\/2018\/05\/building-a-serverless-contact-form-for-your-static-site\/#webpage"},"isPartOf":{"@id":"https:\/\/www.taterboy.com\/blog\/2018\/05\/building-a-serverless-contact-form-for-your-static-site\/#webpage"},"articleSection":"User Experience"},{"@type":"BreadcrumbList","@id":"https:\/\/www.taterboy.com\/blog\/2018\/05\/building-a-serverless-contact-form-for-your-static-site\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog#listItem","position":1,"name":"Home","item":"https:\/\/www.taterboy.com\/blog","nextItem":{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/#listItem","name":"User Experience"}},{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/#listItem","position":2,"name":"User Experience","item":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/","nextItem":{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/2018\/05\/building-a-serverless-contact-form-for-your-static-site\/#listItem","name":"Building A Serverless Contact Form For Your Static Site"},"previousItem":{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/2018\/05\/building-a-serverless-contact-form-for-your-static-site\/#listItem","position":3,"name":"Building A Serverless Contact Form For Your Static Site","previousItem":{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/#listItem","name":"User Experience"}}]},{"@type":"Organization","@id":"https:\/\/www.taterboy.com\/blog\/#organization","name":"Design for Immersive Technologies","description":"User Experience for Games, Virtual Reality (VR) and Augmented\/Mixed Reality (AR\/MR)","url":"https:\/\/www.taterboy.com\/blog\/"},{"@type":"Person","@id":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/#author","url":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/","name":"Guest Contribution","image":{"@type":"ImageObject","@id":"https:\/\/www.taterboy.com\/blog\/2018\/05\/building-a-serverless-contact-form-for-your-static-site\/#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/d2aad73eb0f48c67b141e9fc978a0e498969791ed623346e361d02d19775b0bb?s=96&d=mm&r=g","width":96,"height":96,"caption":"Guest Contribution"}},{"@type":"WebPage","@id":"https:\/\/www.taterboy.com\/blog\/2018\/05\/building-a-serverless-contact-form-for-your-static-site\/#webpage","url":"https:\/\/www.taterboy.com\/blog\/2018\/05\/building-a-serverless-contact-form-for-your-static-site\/","name":"Building A Serverless Contact Form For Your Static Site | Design for Immersive Technologies","description":"Building A Serverless Contact Form For Your Static SiteBuilding A Serverless Contact Form For Your Static Site Brian Holt 2018-05-02T18:30:17+02:002018-05-04T09:06:01+00:00Static site generators provide a fast and simple alternative to Content Management Systems (CMS) like WordPress. There\u2019s no server or database setup, just a build process and simple HTML, CSS, and JavaScript. Unfortunately, without a server,","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/www.taterboy.com\/blog\/#website"},"breadcrumb":{"@id":"https:\/\/www.taterboy.com\/blog\/2018\/05\/building-a-serverless-contact-form-for-your-static-site\/#breadcrumblist"},"author":{"@id":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/#author"},"creator":{"@id":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/#author"},"image":{"@type":"ImageObject","url":"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/form-submission.gif?fit=606%2C532&ssl=1","@id":"https:\/\/www.taterboy.com\/blog\/2018\/05\/building-a-serverless-contact-form-for-your-static-site\/#mainImage","width":606,"height":532},"primaryImageOfPage":{"@id":"https:\/\/www.taterboy.com\/blog\/2018\/05\/building-a-serverless-contact-form-for-your-static-site\/#mainImage"},"datePublished":"2018-05-04T04:19:19-07:00","dateModified":"2018-05-04T04:19:19-07:00"},{"@type":"WebSite","@id":"https:\/\/www.taterboy.com\/blog\/#website","url":"https:\/\/www.taterboy.com\/blog\/","name":"Design for Immersive Technologies","description":"User Experience for Games, Virtual Reality (VR) and Augmented\/Mixed Reality (AR\/MR)","inLanguage":"en-US","publisher":{"@id":"https:\/\/www.taterboy.com\/blog\/#organization"}}]},"og:locale":"en_US","og:site_name":"Design for Immersive Technologies | User Experience for Games, Virtual Reality (VR) and Augmented\/Mixed Reality (AR\/MR)","og:type":"article","og:title":"Building A Serverless Contact Form For Your Static Site | Design for Immersive Technologies","og:description":"Building A Serverless Contact Form For Your Static SiteBuilding A Serverless Contact Form For Your Static Site Brian Holt 2018-05-02T18:30:17+02:002018-05-04T09:06:01+00:00Static site generators provide a fast and simple alternative to Content Management Systems (CMS) like WordPress. There\u2019s no server or database setup, just a build process and simple HTML, CSS, and JavaScript. Unfortunately, without a server,","og:url":"https:\/\/www.taterboy.com\/blog\/2018\/05\/building-a-serverless-contact-form-for-your-static-site\/","article:published_time":"2018-05-04T11:19:19+00:00","article:modified_time":"2018-05-04T11:19:19+00:00","twitter:card":"summary","twitter:title":"Building A Serverless Contact Form For Your Static Site | Design for Immersive Technologies","twitter:description":"Building A Serverless Contact Form For Your Static SiteBuilding A Serverless Contact Form For Your Static Site Brian Holt 2018-05-02T18:30:17+02:002018-05-04T09:06:01+00:00Static site generators provide a fast and simple alternative to Content Management Systems (CMS) like WordPress. There\u2019s no server or database setup, just a build process and simple HTML, CSS, and JavaScript. Unfortunately, without a server,"},"aioseo_meta_data":{"post_id":"46135","title":null,"description":null,"keywords":null,"keyphrases":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_custom_url":null,"og_image_custom_fields":null,"og_custom_image_width":null,"og_custom_image_height":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"location":null,"local_seo":null,"created":"2021-02-08 04:47:15","updated":"2026-08-31 10:36:05","focus_keyword":null,"additional_keywords":null,"truseo_locale":null,"primary_term":null,"og_image_url":null,"og_image_width":null,"og_image_height":null,"twitter_image_url":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"Article","isEnabled":true},"graphs":[]},"limit_modified_date":false,"ai":null,"breadcrumb_settings":null,"seo_analyzer_scan_date":null},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/www.taterboy.com\/blog\" title=\"Home\">Home<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/\" title=\"User Experience\">User Experience<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\tBuilding A Serverless Contact Form For Your Static Site\n\t\t<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"Home","link":"https:\/\/www.taterboy.com\/blog"},{"label":"User Experience","link":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/"},{"label":"Building A Serverless Contact Form For Your Static Site","link":"https:\/\/www.taterboy.com\/blog\/2018\/05\/building-a-serverless-contact-form-for-your-static-site\/"}],"jetpack_publicize_connections":[],"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p8wzr5-c07","jetpack_featured_media_url":"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2018\/05\/form-submission.gif?fit=606%2C532&ssl=1","_links":{"self":[{"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/posts\/46135","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/comments?post=46135"}],"version-history":[{"count":0,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/posts\/46135\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/media\/46136"}],"wp:attachment":[{"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/media?parent=46135"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/categories?post=46135"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/tags?post=46135"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}