loading...
Cover image for Using Netlify lambda functions to send emails from a GatsbyJS site

Using Netlify lambda functions to send emails from a GatsbyJS site

char_bone profile image Charlotte ・4 min read

I’ve recently created my website using GatsbyJS and it’s brilliant. I come from a full stack background, with a lot of back-end experience in PHP so the JAM stack is a different way of thinking for me. However, I’m all about embracing new technology!

My website is fairly simple and doesn’t really need a back-end so it seemed silly to use a CMS with a database; as a developer, I can easily add new information without the need for a fancy admin area. I do however have a contact form, so I needed to decide how I was going to send emails without a backend server.

Netlify does offer built in form handling but I was struggling to get this to work. It actually turns out that Gatsby wasn’t generating the static markup for this form so Netlify didn’t know about it (something to watch out for if your components are being hidden based on state). Also, I wanted the messages to go direct to my inbox, rather than checking on the Netlify site, so I decided lambda functions were the way to go.

Lambda functions basically allow you to create API endpoints without setting up a server. So I just needed to create an endpoint to send emails and call this endpoint when the form is submitted. I decided to use Sendgrid to send out my emails as I really like how you can keep track of activity and they have a really simple node library.

Setting up Netlify functions with Gatsby is really simple and there is already an amazing post on this, so I won’t go into details. Once you’ve ran through the steps in that post, you then just store all of your functions as js files in your lambda directory. Each file must export a handler method with 3 parameters, event, context and callback.

  • event — is an object that contains details about the request, such as body and headers
  • context — contains information about the context in which the function was called, e.g. identity user information
  • callback — is a function that should be called to return either an error (as the first parameter) or a response object. (Although this actually isn’t needed for async calls)

So, to create my lambda function was really simple. I first of all installed and saved the sendgrid dependency using npm and then created a new function called sendmail.js within the lambda folder. One of the reasons why you shouldn’t send things from the client side is because tokens can be stolen easily. Therefore, we need to store the sendgrid API token within Netlify. I also decided to store the email address to send the email message to here, so that it can be updated easily. To access environment variables, go to your Netlify site dashboard, click on settings and click on build & deploy and then environment.

Ok, now we have the environment variables, let’s look at the code

For this function, you can see that we actually only use the event parameter and environment variables (from process.env). The form body is sent as JSON, so we simply parse this and get the values that we need from it. I decided to make it dynamic too and to send all of the fields within the body of my email, this means that if I decided to add another field to the form in future, the function won’t need updating.

The sendgrid documentation doesn’t mention error handling, however I didn’t want to just assume that the message had been sent. I’d rather tell a user that it failed so that I don’t miss out on an important message. I used async await and this caused me a bit of pain as I was getting errors such as UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'statusCode' of undefined when using the callback. It took a bit of hunting around to realise that if you are using an async function, you don’t use the callback, but simply return the response object!

So now we have the function, how do we call it? First of all I created a state object to store all of my form fields. I’m using React hooks, so I’ve written mine this way, however if you are using React classes, simply add to your this.state object.

I then attach a function onto the onChange event for each input element that will update this state.

Finally I created the submit handler below and attached it to onSubmit on my form element. This is just a skeleton, you can do whatever you need to do if the form sends or fails. On my site, I pop up a modal.

And that’s it! simple! Now this is very basic lambda function usage without authentication, etc. If you have functionality that requires authentication, I’d highly recommend reading this post.

Originally posted on Medium

Discussion

pic
Editor guide
Collapse
miguelmota profile image
Miguel Mota

You can move the environment variable code to outside the handler since there's no need to re-set the sendgrid config on every invocation.

const sgMail = require('@sendgrid/mail')

const { SENDGRID_API_KEY, SENDGRID_TO_EMAIL } = process.env
sgMail.setApiKey(SENDGRID_API_KEY)

exports.handler =  async (event, context, callback) => {
 // ..
}
Collapse
char_bone profile image
Charlotte Author

Great catch, thank you! I'll get that updated :)

Collapse
wispyco profile image
Wispy

I'm stuck on the hookState.js part, where do I place that in my gatsby App, same with the onChange.js and submitForm.js. And how do I write my form in order for it to be triggered by the submitForm.js code.

Collapse
wispyco profile image
Wispy

I figured out where to put the code, I had a functional component so I was confused as to how to do forms, because the example's I found used classes. Anyways. I am get the following error from Netlfiy Lambda. Any Ideas?

RangeError [ERR_HTTP_INVALID_STATUS_CODE]: Invalid status code: undefined

Collapse
wispyco profile image
Wispy

I had to add require('dotenv').config(); in my sendmail.js file, because the env variables were not being found. If anyone runs into the same issue. thanks for the tut!

Collapse
marklchaves profile image
mark l chaves

Kudos on your well written & insightful piece. Thanks for posting. Your site rocks BTW.

Collapse
char_bone profile image
Charlotte Author

Sorry for the late response! I've not been on here for a while, but thank you so much :)

Collapse
jwenger100 profile image
Jon Wenger

Were you able to use the free version of Netlify to do this? Or how much is it costing to run the site as described per month?

Collapse
char_bone profile image
Charlotte Author

Yes the free version includes 125,000 requests and 100 hours of runtime :)