Automating Emails with Sendgrid & Next.js

Anabel
6 min readMar 16, 2024

--

Today, we’ll explore a method to capture leads without linking our lead generation form to a database. We’ll also learn how to trigger a custom email campaign programmatically to target those leads, all with Next.js

Please note that to follow along with this tutorial, you’ll need to connect and verify your custom domain with Sendgrid.

Step 1: Obtaining the API key & Setting Up a Contact List

Let’s kick this off by creating a Sendgrid account. To keep things simple in this tutorial, we’ll create a full access API key. Feel free to set up restricted permissions if you’d like. Just remember to store your key safely, like in your .env file, as you’ll only see it once.

Next we’re gonna head over to Marketing -> Contacts to create a new list. Name it and hit save. Once inside your new list, you’ll see its ID in the URL. Copy that ID and store it in your app’s .env file for later use.

To send an email to each new user added to the contact list, we’ll set up an automation with an email template. Go to Marketing -> Automations and select the “Welcome” template or create a custom one. Excellent work!

Step 2: Creating Custom Fields & Fetching Their IDs

Now, you may not need a custom field but if you do we’re covering it in this step. Let’s head to Marketing -> Custom Fields and create a new field selecting its type. I have a couple, two of them are a string, and the one called “budget” is a number.

When grabbing our user’s information via our form, we have to ensure the data matches what our API endpoint wants, so we need to retrieve an ID of each of our custom fields. Since they aren’t lying anywhere in plain sight, we’ll need to shoot a GET request to this specific endpoint to uncover them.

https://api.sendgrid.com/v3/marketing/field_definitions

In our Next.js app, let’s set up a folder named api within /app or /pages, depending on your project structure. Within this api folder, we'll create a new directory, which you can name as you like. I'm naming mine "retrieve". Here, we'll place a route.ts, not a page.tsx, because it will contain our server-side function. For those using JavaScript, remember to append .js to the file name.

Inside app/api/retrieve/route.ts, we'll execute a GET request to fetch the IDs of our custom fields:

import axios from "axios";
import { NextResponse } from "next/server";

export async function GET() {
try {
const response = await axios.get("https://api.sendgrid.com/v3/marketing/field_definitions", {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.SENDGRID_API_KEY}`,
},
});
console.log(response.data)
return NextResponse.json({ response })
} catch(err) {
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 })
}
}

This is a fairly simple code but still let’s break it down:

  • We’re writing an asynchronous function named after our HTTP request, instead of using a default export.
  • We configure headers and handle our Sendgrid authorization by using the API key we stored in our .env .
  • Finally, we return a NextResponse and log our data to the console.

If we visit localhost:3000/api/retrieve, we’re gonna see the output in our terminal. Remember to jot down those IDs, as they'll be essential for our next step :)

Step 3: Creating a Frontend Lead Capture Form

Let’s go ahead and add a simple yet functional form to capture our website visitor’s information inside a page.tsx.

export default function LeadForm() {
//write your data fields object to map over here
//initialize your data object
const [ data, setData ] = useState({
email: "",
full_name: "",
budget: 0,
needs: ""
})
//handle your input change
const handleInputChange = () => {
const { name, value } = e.target
setData(prevState => ({
...prevState,
[name]: name === "budget" ? Number(value) : value
)}
}
/* create your handleSubmit function to send
the client's data to the api endpoint */
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault() //prevent the form from carrying out its default behavior
/* notice how it's a put and not a post request
as that's what Sendgrid expects from us */
try {
await axios.put("/api/sendgrid", data) //our api endpoint
} catch(err) {
console.log(err)
}
}
return (
<>
<form onSubmit={handleSubmit}>
{formFields.map((field, i) => {
<input key={i}
type={field} name={field.name} onChange={handleInputChange} />
}
<button type="submit">Submit</button>
</form>
</>
)
}

That’s a lot going on! Here’s what we’re doing step by step:

  • First, we create a data object that includes the names and types of fields we want in our form.
  • Next, we handle input changes to gather the info typed into our fields, consolidating it into a single data object.
  • Lastly, we implement a basic function to submit this data to our backend, specifically to an endpoint we’ll establish at /api/sendgrid in the upcoming step.

Step 4: Building Our API Endpoint & Sending Data to Sendgrid

To set up our endpoint, we’ll navigate to the api directory and create a folder named "sendgrid". You can choose a different name, but remember to update it in your client code accordingly. Inside /api/sendgrid, we'll execute a PUT request using NextRequest to send our user's email and the information from other custom fields.

import axios from "axios"; //npm install your axios or use fetch if prefer
import { NextRequest, NextResponse } from "next/server";

//don't forget to use a named function after your http request
export async function PUT(req: NextRequest) {
try {
const { email, full_name, budget, needs } = await req.json(); //parse
await axios.put(
"https://api.sendgrid.com/v3/marketing/contacts", //Sendgrid's API
{
contacts: [{ //this is the structure Sendgrid expects
email, //email is their default field
"custom_fields": { //and these are our custom ones
e2_T: full_name, //notice how we're using those IDs we noted down
e1_N: budget,
e3_T: needs
}
}],
list_ids: [process.env.SENDGRID_MAILING_ID], //and that's the list ID
},
{
headers: { //headers for safety
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.SENDGRID_API_KEY}`, //auth
},
}
);
return NextResponse.json({ message: "Successfully sent to the contact list!" });
} catch (err) { //simple error handling
return NextResponse.json({ error: "Internal Server Error" }, { status: 500 });
}
}

Let’s make sure we understand what we wrote:

  • We use axios to make a PUT request to the Sendgrid API to add a lead to our previously created contact list.
  • We parse and deconstruct our request body to access the fields where we stored the user’s information.
  • We match our data object to Sendgrid’s required structure to ensure our data is accepted by their API.
  • We set the request headers to define our data format and authenticated ourselves using our API key.

That’s pretty much it! Go ahead and test your lead form — the entry should show up in your list.

👋 Hey, thanks for reading my article, you can find more interesting stuff on my website. Feel free to connect with me here or here too. See you!

--

--