Tailwind CSS Contact Form with a node.js Form Handler - Part 2
Tags: javascript, node, express, sendgrid
Published: 2021-02-23 19:59:14

**If you followed along with part 1 of this mini-series, you should have a beautiful contact form styled with Tailwind CSS.**

In ***Part 2***, we’re going to build the back-end to handle the form submissions using ***node.js***, ***Express*** and ***SendGrid***.

Download the [SOURCE CODE](https://github.com/brandymedia/tailwind-contact-form-handler)

{% github https://github.com/brandymedia/tailwind-contact-form-handler %}

As we did in ***part 1***, we’re going to be using ***npm*** to manage our dependencies.

##Install Our Dependencies

Create a new project folder `mkdir form-handler`, set this as your current directory `cd form-handler` and then run `npm init -y` to create our package.json file which we will need for our dependencies.

Let's now pull in all the dependencies we're going to need. Run `npm i express @sendgrid/mail dotenv`. We also need a dev dependency, so run `npm i -D nodemon`.

If that all went a bit over your head, don’t worry, I’ll explain what just happened.

In your project directory, you should now see a new directory `node_modules` - this contains all of the dependencies (third party code) we are going to utilise in our project.

If you open up your `package.json` file, you will see we have 3 dependencies under *dependencies* and 1 under *devDependencies*. The first 3 will be utilised in our final code base, the *devDependencies* is only used for development purposes.

If you want to learn more about what each of these dependencies does beyond the scope of this tutorial, then check the links below.

[Express](https://www.npmjs.com/package/express)
[SendGrid](https://www.npmjs.com/package/@sendgrid/mail)
[dotenv](https://www.npmjs.com/package/dotenv)
[nodemon](https://www.npmjs.com/package/nodemon)

Now we have everything installed, there are a couple more things we need to do before we can write the actual code to build the contact form handler.

##Setup and Configure SendGrid
In order to be able to email the form responses, we need a mail service that can handle the form request. In this case, we’re going to use SendGrid.

If you haven't already, head over to [SendGrid](https://sendgrid.com/) and register for a free account.

Once you have access to your account, from the dashboard you will need to create an API Key. Click on ***Settings > API Keys*** and then select ***Create AP Key***.

For the purpose of this tutorial, we only need limited access to the API, so name your key something suitable, so you remember what it's for and then select ***Restricted Access***. Allow access just for ***Mail Send***, then click ***Create & View***.

You will then be presented with your API Key. **Make sure you copy this somewhere safe, as they only show you this once**.

You will also need to [verify your sender identity](https://sendgrid.com/docs/for-developers/sending-email/quickstart-nodejs/#verify-your-sender-identity) to be able to send any emails.

Now we have our API Key and have verified our sender identity, head back to our app and let’s create the file we need to hold this API key. Create a `.env` file in the root of your project `touch .env`.

Open up the file and add our key like so:

```
// .env
SENDGRID_API_KEY=YOUR_KEY_GOES_HERE
```
##Building the Mail Server

Next, let’s create our `app.js` file with `touch app.js` and then open it up.

To be able to use the SendGrid API, we will need to import the SendGrid library, access the SendGrid API Key variable from within our `.env` config file and then tie them together.
```javascript
// app.js
require('dotenv').config();
const sgMail = require('@sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
```

While we’re at it, we may as well import the ***Express*** library and set this up.

```javascript
// app.js
const express = require('express');
const app = express();
```

To make things easier to configure when we push this to production, we will also add a `PORT` variable to our `.env` config file and pull this in to our `app.js`.

```
// .env
PORT=3000
```

```javascript
// app.js
const port = process.env.PORT || 3000;
```

We’re now ready to set up a simple server and add a route using Express, which will allow us to accept a POST request to our app which we can then use to handle the form submission.

In our `app.js` file we have access to *Express* with the `app` variable, so let’s use that to set up the server and create our route.

```javascript
// app.js
app.listen(port, (error) => {
	if (error) throw error;
	console.log(`Listening on port ${port}`);
});

app.post('/’, (req, res) => {
	// handle form submission
});
```

To be able to parse the data we receive from the form submission we need to add a couple of middlewares. Don’t worry too much if you don’t understand what a middleware is or what it does, just know we need these 2 below.

```javascript
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
```

At this point, if we run `node app.js` we should get a console log telling us that our app is ***Listening on port 3000***. This tells us that the server is running. However, we don’t want to have to continually start up the server every time we make a change in our app.

This is why we installed the ***nodemon*** package as a dev dependency earlier.

To set this up, we need to add a couple of custom scripts to our `package.json` file..

```javascript
// package.json
  "scripts": {
    "prod": "node app.js",
    "dev": "nodemon app.js"
  },
```

Notice we added one for ***prod*** and one for ***dev*** - they are very similar apart from one uses ***node*** and the other ***nodemon***.

When building our app locally, we can spin up the server using `npm run dev`. This will allow us to edit our app without having to constantly restart the server. This is because it uses the nodemon package which we set up in the `package.json` file.

So let’s run that now `npm run dev`. You should see a couple of *nodemon* message and then ***Listening on port 3000***, this shows it is working.

At the minute we don’t have any code inside of our *post route* - let’s take a look at that next.

From ***part 1*** of this tutorial, we created a contact form styled with TailwindCSS. If you look back at this code, you will see there are 3 input fields that we need to pull the data from ***message***, ***name*** & ***email***.

When a user submits the forms, these values will be ***POST***ed to our mail server and we can then access these from the ***request*** using `req.body`.

Using destructuring, we can set these values to local variables in our app.

```javascript
const {
	message,
	name,
	email
} = req.body;
```

This effectively looks at the request body and assigns the data to variables with corresponding names.

We can then use the data stored in these variables to compose an email to send to ourselves to get the form submission. We will use [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) to easily allow us to embed variables within our text.

```javascript
    const msg = {
		to: 'test@example.com', // Change to your recipient
		from: 'test@example.com', // Change to your verified sender
		subject: 'Contact Form Submission',
		text: `Message: ${message} Name: ${name} Email: ${email}`,
		html: `
			<strong>Message:</strong>
			<p>${message}</p>
			<strong>Name:</strong>
			<p>${name}</p>
			<strong>Email:</strong>
			<p>${email}</p>
        `,
    }
```
Now we have our data ready to send to SendGrid, let’s use `sgMail` to do just that.

```javascript
    sgMail
        .send(msg)
        .then(() => {
        console.log('Email sent')
        res.end()
    })
        .catch((error) => {
        console.error(error)
        res.end()
    })
```
We should now have a working simple mail server that can handle our submissions from our contact form.

If we’re going to use this mail server in a production environment, then we would need to implement both client-side and server-side validation to make sure we are sending, receiving and handling correctly formatted and safe data.

For now, this is beyond the scope of this tutorial. I may add a third part to the series covering this if there's enough interest.

##Sending a Contact Form Request

Open up the contact form we created in part 1 of the series and update the form action to point to our mail server API and add the method attribute set to post.

```html
<form action="http://localhost:3000" method="post">
```
Make sure in your HTML that the `<button>` type is set to ***submit***.

```html
<button type="submit">
```
Make sure our node server is running with `npm run dev` and then open the contact form in our browser. Add some dummy data to the form fields and hit ***Send***...

...if everything worked correctly, we should see an ***Email sent*** message in our console and have received an email with the data we sent. ***If you have not received the email, check your JUNK folder***.

You may have noticed that the form has redirected to *http://localhost:3000/*. This isn’t great. Ideally, we would redirect back to the original page the form was posted from, or a dedicated ***success*** page.

We could hard code the redirect URL into the Express route, but this isn't that flexible. Instead, we'll add this via a small piece of JavaScript in our contact form.

Inside our `<form>` tag, at the top, add a hidden field - this will hold the URL of the page we submit the form from.

```html
<input type="hidden" name="redirect" class="redirect">
``` 
Now let’s write the JavaScript to populate the hidden field we’ve just added. At the bottom of our code, just inside our `<body>` tag add a script tag with the following code.

```javascript
<script>
    const redirectField = document.querySelector('.redirect');
    const pageUrl = window.location.href;
    redirectField.value = pageUrl;
</script>
```
If you inspect your HTML with your developer tools, you should now see the ***redirect*** input has a value of something like `http://127.0.0.1:5500/public/index.html`. We now need to pull this in to our mail server.

Update our form handler code to add the ***redirect*** value.

```javascript
const {
        message,
        name,
        email,
        redirect
} = req.body;
```

We can now change the `res.end()` to `res.redirect(redirect)` in our send email code.

```javascript
    sgMail
        .send(msg)
        .then(() => {
        console.log('Email sent')
        res.redirect(redirect)
    })
        .catch((error) => {
        console.error(error)
        res.end()
    })
```
This now redirects us back to the page we submitted the form from but from a user experience point of view it is still lacking in terms of any feedback as to whether the form has been submitted successfully. Let’s fix that.

Instead of just redirecting back to the exact URL we got from our redirect variable, we could use a query string to tell the user their form was a ***success***.

Change `res.redirect(redirect)` to `res.redirect(`${redirect}?submit=true`)`.

Now if we submit a form successfully, our mail server will forward us back to our original URL with an additional query string `?submit=true`. We now need to capture this in our form code, so we can show a success message.

Inside our `<form>` tag right at the top, add an empty `div`

```html
<div class="form-message"></div>
```

Then add the JavaScript to handle this.

```javascript
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const formMessage = document.querySelector('.form-message');

if(urlParams.has('submit')) {
    if(urlParams.get('submit') === 'true') {
        formMessage.innerHTML = `<div class="mb-5 p-3 max-w-xl mx-auto shadow-md sm:border-0 md:border md:border-gray-900 md:dark:border-gray-100 bg-green-400 dark:bg-green-400 text-gray-900 dark:text-gray-900">Thanks for your enquiry, someone will be in touch shortly.</div>`;
        formMessage.scrollIntoView();
        window.scrollBy(0, -20);
    }
}
```
Now, whenever the redirect includes `?submit=true` you will be presented with the success message. ***As we have added new classes to our HTML, we will need to make sure we run `npm run build` to make sure these styles are included in the output of our CSS file.***

That wraps it up.

You should now have a fully functioning contact form that can email the submissions to your email address. There are other considerations to make when building publicly accessible forms, like validation, sanitisation etc.

I may look to add a third part to the series, which will cover this in more detail.

{% github https://github.com/brandymedia/tailwind-contact-form-handler %}