I recently redesigned this website using GatsbyJS. Gatsby sites are static sites based on JAMStack. I wanted to add a contact form to my site, but as you know, there is no back end system in a Gatsby site. So, how to implement a contact form that will need to send emails of the responses received?
One way to do that is by using lambda functions. Your form can invoke a URL endpoint, which, when called, executes the function that you wrote. In other words, lambda functions allow you to create API endpoints without setting up a server. This opens up a plethora of possibilities to make “dumb” static sites dynamic without any need of a backend server. Netlify runs these functions on AWS Lambda, and you don’t need to worry about the complexities of AWS as Netlify takes care of it for you.
Netlify functions give us sufficient quota in the free tier. You can run 125,000 function calls with up to 100 hours of runtime per month. It has 128MB of memory and, each function can execute for up to 10 seconds. This is more than enough for me.
So, I had to create an endpoint which will get called upon form submission and accept the form data as input and use SendGrid API to send the form data to me via email.
You can add Netlify functions to Gatsby in 5 steps which are described in detail in the documentation here. I’ll just give a summary of those 5 steps below:
scripts
in package.json
.netlify.toml
file.src/functions
folder.After going through these five steps, you have to add all your functions as .js files in the src/functions
directory. We’ve to follow certain rules for writing functions. Each file must export a handler method with three arguments: event, content, and callback. Following is the general syntax to be followed:
exports.handler = function(event, context, callback) { // your server-side functionality}
Netlify provides the event
and context
parameters when the function is invoked. We provide the callback
parameter.
You will need to create a free account with SendGrid, and get an API key. Next, we will set our SendGrid API Key as an environment variable in Netlify. To set environment variables, go to your Netlify site dashboard, click on Settings, then “Build & deploy” and then “Environment”.
Then, we install the SendGrid mail package.
npm install --save @sendgrid/mail
Now, let’s take a look at our function sendmail.js
// src/functions/sendmail.jsconst sgMail = require('@sendgrid/mail');sgMail.setApiKey(process.env.SENDGRID_API_KEY);
exports.handler = async (event, context, callback) => {
const data = JSON.parse(event.body) const { email, subject } = data
const body = Object.keys(data).map((k) => { return `${k}: ${data[k]}` }).join("<br><br>");
const mail_to_send = { from: email, subject: subject ? subject : 'New Entry from Contact Form', html: body, };
try{ await sgMail.send(mail_to_send)
return { statusCode: 200, body: "Message sent successfully" } } catch(e){ return { statusCode: e.code, body: e.message } }};
The code is pretty straight forward. First, we set the API Key as specified according to SendGrid documentation, then, we collect the form body which has been sent as JSON, make the object “mail_to_send”, and send it. You might have noticed we have used async/await
instead of a normal call. One thing to note is that when you use async await in a Lambda Function, there is no need to use a callback function, you just return the response object. In this code, we are returning “Message Sent Successfully” if everything goes right and if there’s any error, we return the error.
Now that our function is ready, we will go ahead and create a Contact Form in Gatsby using React.
First, the HTML part:
<form onSubmit={this.handleSubmit} class={"contactForm"}> <h2>Drop a message</h2> <p type="Name:"> <input required placeholder="What's your name?" name={"name"} type={"text"} value={this.state.name} onChange={this.handleChange}/> </p>
<p type="Email:"> <input required placeholder="How shall contact you back?" name={"email"} type={"email"} value={this.state.email} onChange={this.handleChange}/> </p>
<p type="Subject:"> <input required placeholder="What is this regarding?" name={"subject"} type={"text"} value={this.state.subject} onChange={this.handleChange}/> </p>
<p type="Message:"> <textarea required placeholder="How can I help?" rows={3} name={"message"} value={this.state.message} onChange={this.handleChange}/> </p>
<button type={"submit"}>Submit</button></form>
Next, the handleChange function:
handleChange(e){this.setState({ [e.target.name]:e.target.value,})}
Then we look at the handleSubmit function where we actually send the email containing form data
async handleSubmit(e){ e.preventDefault();
const {name, email, subject, message} = this.state; const to_send = { name: name, email: email, subject : subject, message : message }
try{ const response = await fetch("/.netlify/functions/sendmail", { method: "POST", body: JSON.stringify(to_send), } )
if (!response.ok){ //Do something when request fails return } //Do something when request is successful } catch (e) { console.log(e); }}
As you can see, the function is pretty straight-forward. The above code is a skeleton, you can put in the code for whatever you want to do when sending the mail fails or is successful. I have implemented a CSS spinner while the fetch request is going on, and then a message is shown depending on whether the request has failed or not. You can see the complete code of my contact form here.
That’s it for this post, I plan to make another post on integrating reCaptcha to avoid spam in the near future, so stay tuned!