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.
Top comments (11)
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.
Great catch, thank you! I'll get that updated :)
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.
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
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!
Kudos on your well written & insightful piece. Thanks for posting. Your site rocks BTW.
Sorry for the late response! I've not been on here for a while, but thank you so much :)
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?
Yes the free version includes 125,000 requests and 100 hours of runtime :)
Still useful in 2021 🙂 I had a problem with getting the data from the form in nice, structured way. Problem solved