Create and Deploy a SendGrid-Powered Next.js and Node.js Contact Form with ZEIT Now

Create a SendGrid-Powered Next.js and Node.js Contact Form and deploy it with ZEIT Now.

SendGrid is a cloud-based SMTP provider that allows you to send email without having to maintain email servers.

This guide walks you through creating a Next.js app with a contact form powered by SendGrid, and how to deploy it with ZEIT Now.

Step 1: Creating a SendGrid API Key

To start, you need to have created a SendGrid account. Once logged in to SendGrid, follow the setup guide steps for integrating using the Web API, choosing the Node.js option.

You will be asked to supply a name for your API key and given the option to create it.

Once created, make a note of this API key as it will not be shown again, this will be used in the app you create.

Note: On occasion, it can take a few hours for the API key to become active.

Step 2: Creating Your Next.js App

Get started creating your Next.js app by making a project directory with the required structure and moving into it:

mkdir -p nextjs-sendgrid/pages/api && cd nextjs-sendgrid

Creating and entering into the /nextjs-sendgrid directory.

Next, initialize the project:

npm init -y

Initializing the project, this creates a package.json file.

Continue to install the SendGrid Node.js helper which allows you to send email using your API key, along with the required dependencies for Next.js including dotenv for using your API key during local development:

npm i @sendgrid/mail dotenv next react react-dom

Adding the @sendgrid/mail, dotenv, next, react and react-dom dependencies to the project.

Inside of the /pages directory, create an index.js file with the code below:

import React, { useState } from 'react'

export default () => {
  const [status, setStatus] = useState({
    submitted: false,
    submitting: false,
    info: { error: false, msg: null }
  })

  const [inputs, setInputs] = useState({
    email: '',
    message: ''
  })

  const handleResponse = (status, msg) => {
    if (status === 200) {
      setStatus({
        submitted: true,
        submitting: false,
        info: { error: false, msg: msg }
      })
      setInputs({
        email: '',
        message: ''
      })
    } else {
      setStatus({
        info: { error: true, msg: msg }
      })
    }
  }

  const handleOnChange = e => {
    e.persist()
    setInputs(prev => ({
      ...prev,
      [e.target.id]: e.target.value
    }))
    setStatus({
      submitted: false,
      submitting: false,
      info: { error: false, msg: null }
    })
  }

  const handleOnSubmit = async e => {
    e.preventDefault()
    setStatus(prevStatus => ({ ...prevStatus, submitting: true }))
    const res = await fetch('/api/send', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(inputs)
    })
    const text = await res.text()
    handleResponse(res.status, text)
  }

  return (
    <main>
      <form onSubmit={handleOnSubmit}>
        <label htmlFor="email">Email</label>
        <input
          id="email"
          type="email"
          onChange={handleOnChange}
          required
          value={inputs.email}
        />
        <label htmlFor="message">Message</label>
        <textarea
          id="message"
          onChange={handleOnChange}
          required
          value={inputs.message}
        />
        <button type="submit" disabled={status.submitting}>
          {!status.submitting
            ? !status.submitted
              ? 'Submit'
              : 'Submitted'
            : 'Submitting...'}
        </button>
      </form>
      {status.info.error && (
        <div className="error">Error: {status.info.msg}</div>
      )}
      {!status.info.error && status.info.msg && (
        <div className="success">{status.info.msg}</div>
      )}
    </main>
  )
}

Adding a /pages/index.js file to the project.

Add development and build scripts to the package.json file. These allow you to run your app with a development server and tell ZEIT Now how to build your project for deployment:

{
  ...
  "scripts": {
    "dev": "next dev",
    "build": "next build"
  }
}

Adding dev and build scripts to the package.json file.

Note: If you wish to use the same styles as the example app, you can find them here.

Step 3: Writing the Serverless Function

Create the Node.js API endpoint that will be used to send the form data to the SendGrid API by adding a send.js file to the /pages/api directory with the following code:

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

export default async function(req, res) {
  sgMail.setApiKey(process.env.SENDGRID_API_KEY)

  const { email, message } = req.body

  const content = {
    to: '[your-email-address]',
    from: email,
    subject: `New Message From - ${email}`,
    text: message,
    html: `<p>${message}</p>`
  }

  try {
    await sgMail.send(content)
    res.status(200).send('Message sent successfully.')
  } catch (error) {
    console.log('ERROR', error)
    res.status(400).send('Message not sent.')
  }
}

An example /pages/api/send.js file that sends form data to the SendGrid API.

Note: Make sure you change the to value to the required destination for emails sent.

Create a .env file, adding your SendGrid API key where prompted:

SENDGRID_API_KEY=[your-sendgrid-api-key]

An example .env file that stores environment variables for use during local development.

Add a next.config.js file that will provide your app with the defined environment variable:

require('dotenv').config()

module.exports = {
  env: {
    SENDGRID_API_KEY: process.env.SENDGRID_API_KEY
  }
}

An example next.config.js file that provides environment variables to the app.

Note: The app can now be run in development by using npm run dev.

Create a Now Secret to securely store the SendGrid API key, this will be used when deploying the app with ZEIT Now.

now secrets add SENDGRID_API_KEY [your-sendgrid-api-key]

Adding the access key as a Now Secret.

Lastly, to make the Now Secret available to the Serverless Function when deployed, create a now.json file:

{
  "build": {
    "env": {
      "SENDGRID_API_KEY": "@sendgrid_api_key"
    }
  }
}

An example now.json file that makes a Now Secret available to the app.

Step 4: Deploying the App with ZEIT Now

With the project complete, you are ready to deploy it with ZEIT Now.

If you have not yet installed Now, you can do so by installing Now CLI.

You can now deploy the project with a single command:

now

Deploying the project with the now command.

To deploy your Next.js, Node.js, and SendGrid project from a Git repository, you can use either Now for GitHub or Now for GitLab to have your project automatically deployed on every push, and the production domain updated on push to master.



Written By
Written by msweeneydevmsweeneydev
on October 10th 2019