<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ sendgrid - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ sendgrid - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Mon, 24 Aug 2026 07:41:05 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/sendgrid/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Contact Form with SendGrid and Next.js ]]>
                </title>
                <description>
                    <![CDATA[ By Manu Arora Contact forms are useful on websites if you want your users to be able to interact with you over email. And there are lots of ways to build them. Traditionally you'd have either used PHP for sending emails over the server or some third ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-working-contact-form-with-sendgrid-and-next-js/</link>
                <guid isPermaLink="false">66d46017d14641365a050915</guid>
                
                    <category>
                        <![CDATA[ email ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ sendgrid ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 30 Aug 2021 15:24:14 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/08/Blue-and-White-Modern-Corporate-Travel-YouTube-Thumbnail--5-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Manu Arora</p>
<p>Contact forms are useful on websites if you want your users to be able to interact with you over email. And there are lots of ways to build them.</p>
<p>Traditionally you'd have either used PHP for sending emails over the server or some third party service that would take care of the email logic. </p>
<p>But in this article, we are going to talk about how to send emails from your Next.js application with the SendGrid API.</p>
<p>We are going to create a simple page – a contact form built with React – that has input fields which we'll validate before sending. We'll connect the form to the SendGrid API which will take care of sending the emails to you. Then, at the end of the day, all you have to do is check your email to find those queries.</p>
<p>However, if you don't have a Next.js project yet, you can easily create one and integrate it with Vercel by following the below mentioned steps:</p>
<ol>
<li>Create an account on <a target="_blank" href="https://vercel.com">Vercel</a> and click on <code>New Project</code></li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screenshot-2021-08-30-at-9.41.17-AM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="2">
<li>Choose the template as <code>Next.js</code>:</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screenshot-2021-08-30-at-9.37.17-AM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="3">
<li>Name your repository anything you would like and click on create project. (Choose GitHub, GitLab or BitBucket for your remote code versioning)</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screenshot-2021-08-30-at-9.37.34-AM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Following the above three points, you'll have a repository on your versioning account.</p>
<h2 id="heading-the-tech-stack-well-use">The Tech Stack We'll Use</h2>
<ul>
<li><a target="_blank" href="https://nextjs.org">Next.js</a> for creating a contact form landing page</li>
<li><a target="_blank" href="https://tailwindcss.com">TailwindCSS</a> for styling the components</li>
<li><a target="_blank" href="https://sendgrid.com">SendGrid</a> for sending emails using their APIs</li>
<li><a target="_blank" href="https://vercel.com">Vercel</a> for hosting our application and CI/CD</li>
</ul>
<p>We are going to use Nextjs' API routes to handle form events. The API modules provide a flexible way to handle backend logic in our Next.js application. </p>
<p>Whatever code we write in the API folder will get deployed as a Serverless function onto Vercel for hosting. You can read more about Next.js API routes <a target="_blank" href="https://nextjs.org/docs/api-routes/introduction">here</a></p>
<p>If you already have a Next.js project where you want to set up a working contact form, that's great. In that case, it'll be easy for you to create pages and get started right away. </p>
<p>But if you don't have a project set up yet, that's ok – go to Vercel and create a Next.js starter project and clone the repository.</p>
<h2 id="heading-application-flow">Application Flow</h2>
<p>Let's take a look at the application flow – or how the sending of emails actually works:</p>
<ul>
<li>The end-user fills in the mandotary 4 fields and clicks on submit.</li>
<li>On submit, the <code>handleSubmit</code> function gets triggered.</li>
<li><code>handleSubmit</code> validates the form for input fields and checks if they are not empty.</li>
<li>If the form fields are not empty, an API call is made to <code>api/sendgrid</code> where the logic of sending emails lives.</li>
<li>In <code>api/sendgrid</code>, the <code>@sendgrid/mail</code> module initializes a <code>send</code> function that takes it your application's API keys and sends the email with the required fields.</li>
<li>If email is successfully delivered, a <code>200</code> response is sent to the client, else a <code>400</code> response is sent to the client.</li>
<li>Responses are handled at the frontend and the appropriate messages are displayed. </li>
</ul>
<h2 id="heading-how-to-set-up-tailwindcss">How to Set Up TailwindCSS</h2>
<p>Setting up TailwindCSS is quite easy, and you can do it in two simple ways.</p>
<ol>
<li>Install TailwindCSS as a dependency in your project:</li>
</ol>
<pre><code class="lang-bash">npm install -D tailwindcss@latest postcss@latest autoprefixer@latest
</code></pre>
<ol start="2">
<li>Initialize a TailwindCSS configuration file for your project. This will create a <code>tailwind.config.js</code> file in the root directory:</li>
</ol>
<pre><code class="lang-bash">npx tailwindcss init
</code></pre>
<p>Then you'll need to edit the configuration file, include <code>purge</code> paths, and enable <code>jit</code> mode:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">module</span>.exports = {
   <span class="hljs-attr">purge</span>: [],
   <span class="hljs-attr">mode</span>: <span class="hljs-string">'jit'</span>,
   <span class="hljs-attr">purge</span>: [<span class="hljs-string">'./pages/**/*.{js,ts,jsx,tsx}'</span>, <span class="hljs-string">'./components/**/*.{js,ts,jsx,tsx}'</span>],
    <span class="hljs-attr">darkMode</span>: <span class="hljs-literal">false</span>, <span class="hljs-comment">// or 'media' or 'class'</span>
    <span class="hljs-attr">theme</span>: {
      <span class="hljs-attr">extend</span>: {},
    },
    <span class="hljs-attr">variants</span>: {
      <span class="hljs-attr">extend</span>: {},
    },
    <span class="hljs-attr">plugins</span>: [],
  }
</code></pre>
<p>You use <code>purge</code> to remove unwanted styles from your project at build time. It is helpful if you want to reduce the CSS bundle size. </p>
<p><code>jit</code> is the new TailwindCSS mode where you can specify dynamic classnames in the code itself. </p>
<p>For example, if you want to have your text size be <code>10px</code> (which is not already there in the TailwindCSS modules), you can write <code>text-[10px]</code> in your class names and it will reflect automatically. No need to write custom style attributes anymore. 💯</p>
<p>Next, import Tailwind styles in your root <code>_app.js</code> file:</p>
<pre><code class="lang-js"><span class="hljs-comment">// pages/_app.js</span>
 <span class="hljs-keyword">import</span> <span class="hljs-string">'../styles/globals.css'</span>
 <span class="hljs-keyword">import</span> <span class="hljs-string">'tailwindcss/tailwind.css'</span>

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyApp</span>(<span class="hljs-params">{ Component, pageProps }</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Component</span> {<span class="hljs-attr">...pageProps</span>} /&gt;</span></span>
  }

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> MyApp
</code></pre>
<p>Then include Tailwind's core CSS in your root level stylesheet like this:</p>
<pre><code class="lang-js"><span class="hljs-comment">/* ./styles/globals.css */</span>
@tailwind base;
@tailwind components;
@tailwind utilities;
</code></pre>
<p>With this, you have successfully setup TailwindCSS for your project.</p>
<h2 id="heading-the-markup-and-styling-for-the-contact-page">The Markup and Styling for the Contact Page</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screenshot-2021-08-25-at-12.08.53-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>We'll build the webpage completely with Tailwind. I got the page itself directly from the <a target="_blank" href="https://tailwindmasterkit.com">Tailwind Master Kit</a> which is a components and templates library for Tailwind web projects.</p>
<p>Let's go through the HTML of the page (essentially the contact form) to understand how everything's implemented:</p>
<pre><code class="lang-js">&lt;form <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"rounded-lg shadow-xl flex flex-col px-8 py-8 bg-white dark:bg-blue-500"</span>&gt;
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-2xl font-bold dark:text-gray-50"</span>&gt;</span>Send a message<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span></span>

      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"fullname"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-gray-500 font-light mt-8 dark:text-gray-50"</span>&gt;</span>Full name<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-red-500 dark:text-gray-50"</span>&gt;</span>*<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span></span>
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"fullname"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-transparent border-b py-2 pl-4 focus:outline-none focus:rounded-md focus:ring-1 ring-green-500 font-light text-gray-500"</span> /&gt;</span></span>

      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-gray-500 font-light mt-4 dark:text-gray-50"</span>&gt;</span>E-mail<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-red-500"</span>&gt;</span>*<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span></span>
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-transparent border-b py-2 pl-4 focus:outline-none focus:rounded-md focus:ring-1 ring-green-500 font-light text-gray-500"</span> /&gt;</span></span>

      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"subject"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-gray-500 font-light mt-4 dark:text-gray-50"</span>&gt;</span>Subject<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-red-500"</span>&gt;</span>*<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span></span>
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"subject"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-transparent border-b py-2 pl-4 focus:outline-none focus:rounded-md focus:ring-1 ring-green-500 font-light text-gray-500"</span> /&gt;</span></span>

      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"message"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-gray-500 font-light mt-4 dark:text-gray-50"</span>&gt;</span>Message<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-red-500"</span>&gt;</span>*<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span></span>
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"message"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-transparent border-b py-2 pl-4 focus:outline-none focus:rounded-md focus:ring-1 ring-green-500 font-light text-gray-500"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</span>&gt;</span></span>
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex flex-row items-center justify-start"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-10 mt-8 py-2 bg-[#130F49] text-gray-50 font-light rounded-md text-lg flex flex-row items-center"</span>&gt;</span>
          Send
          <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"24"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"24"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 24 24"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-cyan-500 ml-2"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M9.00967 5.12761H11.0097C12.1142 5.12761 13.468 5.89682 14.0335 6.8457L16.5089 11H21.0097C21.562 11 22.0097 11.4477 22.0097 12C22.0097 12.5523 21.562 13 21.0097 13H16.4138L13.9383 17.1543C13.3729 18.1032 12.0191 18.8724 10.9145 18.8724H8.91454L12.4138 13H5.42485L3.99036 15.4529H1.99036L4.00967 12L4.00967 11.967L2.00967 8.54712H4.00967L5.44417 11H12.5089L9.00967 5.12761Z"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span> /&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    &lt;/form&gt;
</code></pre>
<p>The form has 4 fields:</p>
<ul>
<li>Full Name</li>
<li>Email</li>
<li>Subject</li>
<li>Message</li>
</ul>
<p>All the fields are mandatory – and we'll validate them too later on. You'll expect your user to provide you with all their details while sending the email.</p>
<p>To capture the fields, we are going to use React's <a target="_blank" href="https://reactjs.org/docs/hooks-state.html">useState()</a> hook to ensure that our data is persisted in the application.</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ContactUs</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [fullname, setFullname] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [email, setEmail] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [subject, setSubject] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [message, setMessage] = useState(<span class="hljs-string">""</span>);

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">form</span>
          <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{handleSubmit}</span>
          <span class="hljs-attr">className</span>=<span class="hljs-string">"rounded-lg shadow-xl flex flex-col px-8 py-8 bg-white dark:bg-blue-500"</span>
        &gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-2xl font-bold dark:text-gray-50"</span>&gt;</span>
            Send a message
          <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

          <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
            <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"fullname"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-500 font-light mt-8 dark:text-gray-50"</span>
          &gt;</span>
            Full name<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500 dark:text-gray-50"</span>&gt;</span>*<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{fullname}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> {
              setFullname(e.target.value);
            }}
            name="fullname"
            className="bg-transparent border-b py-2 pl-4 focus:outline-none focus:rounded-md focus:ring-1 ring-green-500 font-light text-gray-500"
          /&gt;


          <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
            <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"email"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-500 font-light mt-4 dark:text-gray-50"</span>
          &gt;</span>
            E-mail<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500"</span>&gt;</span>*<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span>
            <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{email}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> {
              setEmail(e.target.value);
            }}
            className="bg-transparent border-b py-2 pl-4 focus:outline-none focus:rounded-md focus:ring-1 ring-green-500 font-light text-gray-500"
          /&gt;


          <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
            <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"subject"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-500 font-light mt-4 dark:text-gray-50"</span>
          &gt;</span>
            Subject<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500"</span>&gt;</span>*<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
            <span class="hljs-attr">name</span>=<span class="hljs-string">"subject"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{subject}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> {
              setSubject(e.target.value);
            }}
            className="bg-transparent border-b py-2 pl-4 focus:outline-none focus:rounded-md focus:ring-1 ring-green-500 font-light text-gray-500"
          /&gt;

          <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
            <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"message"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-500 font-light mt-4 dark:text-gray-50"</span>
          &gt;</span>
            Message<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500"</span>&gt;</span>*<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span>
            <span class="hljs-attr">name</span>=<span class="hljs-string">"message"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{message}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> {
              setMessage(e.target.value);
            }}
            className="bg-transparent border-b py-2 pl-4 focus:outline-none focus:rounded-md focus:ring-1 ring-green-500 font-light text-gray-500"
          &gt;<span class="hljs-tag">&lt;/<span class="hljs-name">textarea</span>&gt;</span>

          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-row items-center justify-start"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
              <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"px-10 mt-8 py-2 bg-[#130F49] text-gray-50 font-light rounded-md text-lg flex flex-row items-center"</span>
            &gt;</span>
              Submit
              <span class="hljs-tag">&lt;<span class="hljs-name">svg</span>
                <span class="hljs-attr">width</span>=<span class="hljs-string">"24"</span>
                <span class="hljs-attr">height</span>=<span class="hljs-string">"24"</span>
                <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 24 24"</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"text-cyan-500 ml-2"</span>
                <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span>
                <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span>
              &gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
                  <span class="hljs-attr">d</span>=<span class="hljs-string">"M9.00967 5.12761H11.0097C12.1142 5.12761 13.468 5.89682 14.0335 6.8457L16.5089 11H21.0097C21.562 11 22.0097 11.4477 22.0097 12C22.0097 12.5523 21.562 13 21.0097 13H16.4138L13.9383 17.1543C13.3729 18.1032 12.0191 18.8724 10.9145 18.8724H8.91454L12.4138 13H5.42485L3.99036 15.4529H1.99036L4.00967 12L4.00967 11.967L2.00967 8.54712H4.00967L5.44417 11H12.5089L9.00967 5.12761Z"</span>
                  <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span>
                /&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span></span>
    )
}
</code></pre>
<p>Notice the form attribute <code>onSubmit={handleSubmit}</code>. This is the function where we are going to actually send the email through SendGrid. But before that, let's create a SendGrid Project and retrieve the <code>API keys</code>.</p>
<h2 id="heading-how-to-set-up-a-sendgrid-project">How to Set Up a SendGrid Project</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screenshot-2021-08-25-at-1.10.59-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>First, you just need to head over to SendGrid's <a target="_blank" href="https://signup.sendgrid.com/">homepage</a> and signup for an account (if you don't have one already).</p>
<p>After successfully creating an account, register for an API Key. You can do it <a target="_blank" href="https://app.sendgrid.com/guide/integrate/langs/nodejs">here</a>.</p>
<p>Sendgrid requires you to create a Sender Identity to protect against spam and malicious mails. To do so, go to the <a target="_blank" href="https://app.sendgrid.com/settings/sender_auth">Sendgrid Identity page</a> and click on <code>Create New Sender</code> to create a sender identity.</p>
<p>You'll be rqeuired to fill out a detailed form. Just complete the form and hit submit. Finally, just verify your email address and you're done.</p>
<p>Once you've retrieved the <code>API keys</code>, create a <code>.env.local</code> file in your local environment and paste the following code:</p>
<pre><code class="lang-js">SENDGRID_API_KEY= YOUR_API_KEY_HERE
</code></pre>
<p>Replace <code>YOUR_API_KEY_HERE</code> with the API key you just retrieved.</p>
<h2 id="heading-how-to-create-a-serverless-api-route">How to Create a Serverless API Route</h2>
<p>Creating a serverless API route is quite easy with Next.js.</p>
<p>Go to <code>/pages/api</code> and inside the <code>api</code> folder create a file called <code>sendgrid.js</code>.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> sendgrid <span class="hljs-keyword">from</span> <span class="hljs-string">"@sendgrid/mail"</span>;

sendgrid.setApiKey(process.env.SENDGRID_API_KEY);

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sendEmail</span>(<span class="hljs-params">req, res</span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-comment">// console.log("REQ.BODY", req.body);</span>
    <span class="hljs-keyword">await</span> sendgrid.send({
      <span class="hljs-attr">to</span>: <span class="hljs-string">"mannuarora7000@gmail.com"</span>, <span class="hljs-comment">// Your email where you'll receive emails</span>
      <span class="hljs-attr">from</span>: <span class="hljs-string">"manuarorawork@gmail.com"</span>, <span class="hljs-comment">// your website email address here</span>
      <span class="hljs-attr">subject</span>: <span class="hljs-string">`<span class="hljs-subst">${req.body.subject}</span>`</span>,
      <span class="hljs-attr">html</span>: <span class="hljs-string">`&lt;div&gt;You've got a mail&lt;/div&gt;`</span>,
    });
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-comment">// console.log(error);</span>
    <span class="hljs-keyword">return</span> res.status(error.statusCode || <span class="hljs-number">500</span>).json({ <span class="hljs-attr">error</span>: error.message });
  }

  <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">error</span>: <span class="hljs-string">""</span> });
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> sendEmail;
</code></pre>
<p>SendGrid requires us to initialize a <code>sendgrid</code> object with the API keys with the <code>setApiKey()</code> method. Initialize the object with your API key and you can send emails with the <code>send()</code> method.</p>
<p>There are essentially four fields that are required in the <code>send()</code> method's body:</p>
<ul>
<li><code>to</code> – The email address where you want your email to get delivered</li>
<li><code>from</code> – Your SendGrid Email that you used for Sender Identity Verification. Your emails will be sent from this email.</li>
<li><code>subject</code> – The subject line of the email</li>
<li><code>message</code> – the message body of the email</li>
</ul>
<p>We are going to construct these four parameters ourselves so that we can understand our emails better. Here is the updated code from the same snippet above:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> sendgrid <span class="hljs-keyword">from</span> <span class="hljs-string">"@sendgrid/mail"</span>;

sendgrid.setApiKey(process.env.SENDGRID_API_KEY);

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sendEmail</span>(<span class="hljs-params">req, res</span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">await</span> sendgrid.send({
      <span class="hljs-attr">to</span>: <span class="hljs-string">"youremail@gmail.com"</span>, <span class="hljs-comment">// Your email where you'll receive emails</span>
      <span class="hljs-attr">from</span>: <span class="hljs-string">"youremail@gmail.com"</span>, <span class="hljs-comment">// your website email address here</span>
      <span class="hljs-attr">subject</span>: <span class="hljs-string">`[Lead from website] : <span class="hljs-subst">${req.body.subject}</span>`</span>,
      <span class="hljs-attr">html</span>: <span class="hljs-string">`&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
      &lt;html lang="en"&gt;
      &lt;head&gt;
        &lt;meta charset="utf-8"&gt;

        &lt;title&gt;The HTML5 Herald&lt;/title&gt;
        &lt;meta name="description" content="The HTML5 Herald"&gt;
        &lt;meta name="author" content="SitePoint"&gt;
      &lt;meta http-equiv="Content-Type" content="text/html charset=UTF-8" /&gt;

        &lt;link rel="stylesheet" href="css/styles.css?v=1.0"&gt;

      &lt;/head&gt;

      &lt;body&gt;
        &lt;div class="img-container" style="display: flex;justify-content: center;align-items: center;border-radius: 5px;overflow: hidden; font-family: 'helvetica', 'ui-sans';"&gt;              
              &lt;/div&gt;
              &lt;div class="container" style="margin-left: 20px;margin-right: 20px;"&gt;
              &lt;h3&gt;You've got a new mail from <span class="hljs-subst">${req.body.fullname}</span>, their email is: ✉️<span class="hljs-subst">${req.body.email}</span> &lt;/h3&gt;
              &lt;div style="font-size: 16px;"&gt;
              &lt;p&gt;Message:&lt;/p&gt;
              &lt;p&gt;<span class="hljs-subst">${req.body.message}</span>&lt;/p&gt;
              &lt;br&gt;
              &lt;/div&gt;
              &lt;img src="https://manuarora.in/logo.png" class="logo-image" style="height: 50px;width: 50px;border-radius: 5px;overflow: hidden;"&gt;
              &lt;p class="footer" style="font-size: 16px;padding-bottom: 20px;border-bottom: 1px solid #D1D5DB;"&gt;Regards&lt;br&gt;Manu Arora&lt;br&gt;Software Developer&lt;br&gt;+91 9587738861&lt;/p&gt;
              &lt;div class="footer-links" style="display: flex;justify-content: center;align-items: center;"&gt;
                &lt;a href="https://manuarora.in/" style="text-decoration: none;margin: 8px;color: #9CA3AF;"&gt;Website&lt;/a&gt;
                &lt;a href="https://manuarora.in/blog/" style="text-decoration: none;margin: 8px;color: #9CA3AF;"&gt;Blog&lt;/a&gt;
                &lt;a href="https://github.com/manuarora700/" style="text-decoration: none;margin: 8px;color: #9CA3AF;"&gt;GitHub&lt;/a&gt;
                &lt;a href="https://instagram.com/maninthere/" style="text-decoration: none;margin: 8px;color: #9CA3AF;"&gt;Instagram&lt;/a&gt;
                &lt;a href="https://linkedin.com/in/manuarora28/" style="text-decoration: none;margin: 8px;color: #9CA3AF;"&gt;LinkedIn&lt;/a&gt;
                &lt;a href="https://twitter.com/mannupaaji/" style="text-decoration: none;margin: 8px;color: #9CA3AF;"&gt;Twitter&lt;/a&gt;

              &lt;/div&gt;
              &lt;/div&gt;
      &lt;/body&gt;
      &lt;/html&gt;`</span>,
    });
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-comment">// console.log(error);</span>
    <span class="hljs-keyword">return</span> res.status(error.statusCode || <span class="hljs-number">500</span>).json({ <span class="hljs-attr">error</span>: error.message });
  }

  <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">error</span>: <span class="hljs-string">""</span> });
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> sendEmail;
</code></pre>
<p>If you want to send <code>html</code> in the email body, you will have to use inline styles which are there in the example as well.</p>
<p>Here, we are essentially using SendGrid's <code>send()</code> method provided by the SendGrid API to send emails. We use the <code>send()</code> method with the <code>sendgrid</code> object which we initialized with the API key. This makes sure that our emails are secure and delivered only by our permission.</p>
<p>Also, we've wrapped the code in a <code>try - catch</code> block. This ensures that our application can handle exceptions and errors correctly. If by any change the email sending fails, then the code immediately falls into the <code>catch()</code> block and we return an <code>error</code> object. This signifies that there has been some problem on the back-end.</p>
<p>Looking at the API response from the back-end, the front-end responds accordingly and the UI changes.</p>
<p>The styling goes into the <code>html</code> attribute inside of the <code>send()</code> method body. How you want to style your email completely depends on you. Here, I've included a simple template with a footer to my Twitter, Instagram, GitHub and website, along with the original message body that the end-user sends.</p>
<p>Now our API route is setup, so let's move on to the front end and learn how to handle the response correctly.</p>
<h2 id="heading-how-to-call-the-api-and-handle-responses">How to Call the API and Handle Responses</h2>
<p>Since our API route is setup, we are now going to call our serverless API and fetch the response.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> React, { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ContactUs</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [fullname, setFullname] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [email, setEmail] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [subject, setSubject] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [message, setMessage] = useState(<span class="hljs-string">""</span>);



  <span class="hljs-keyword">const</span> handleSubmit = <span class="hljs-keyword">async</span> (e) =&gt; {
    e.preventDefault();

    <span class="hljs-keyword">let</span> isValidForm = handleValidation();


      <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"/api/sendgrid"</span>, {
        <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({
          <span class="hljs-attr">email</span>: email,
          <span class="hljs-attr">fullname</span>: fullname,
          <span class="hljs-attr">subject</span>: subject,
          <span class="hljs-attr">message</span>: message,
        }),
        <span class="hljs-attr">headers</span>: {
          <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
        },
        <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
      });

      <span class="hljs-keyword">const</span> { error } = <span class="hljs-keyword">await</span> res.json();
      <span class="hljs-keyword">if</span> (error) {
        <span class="hljs-built_in">console</span>.log(error);
        <span class="hljs-keyword">return</span>;
      }
    <span class="hljs-built_in">console</span>.log(fullname, email, subject, message);
  };
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"rounded-lg shadow-xl flex flex-col px-8 py-8 bg-white dark:bg-blue-500"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-2xl font-bold dark:text-gray-50"</span>&gt;</span>Send a message<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"fullname"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-gray-500 font-light mt-8 dark:text-gray-50"</span>&gt;</span>Full name<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-red-500 dark:text-gray-50"</span>&gt;</span>*<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"fullname"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-transparent border-b py-2 pl-4 focus:outline-none focus:rounded-md focus:ring-1 ring-green-500 font-light text-gray-500"</span> /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-gray-500 font-light mt-4 dark:text-gray-50"</span>&gt;</span>E-mail<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-red-500"</span>&gt;</span>*<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-transparent border-b py-2 pl-4 focus:outline-none focus:rounded-md focus:ring-1 ring-green-500 font-light text-gray-500"</span> /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"subject"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-gray-500 font-light mt-4 dark:text-gray-50"</span>&gt;</span>Subject<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-red-500"</span>&gt;</span>*<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"subject"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-transparent border-b py-2 pl-4 focus:outline-none focus:rounded-md focus:ring-1 ring-green-500 font-light text-gray-500"</span> /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"message"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-gray-500 font-light mt-4 dark:text-gray-50"</span>&gt;</span>Message<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-red-500"</span>&gt;</span>*<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"message"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-transparent border-b py-2 pl-4 focus:outline-none focus:rounded-md focus:ring-1 ring-green-500 font-light text-gray-500"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex flex-row items-center justify-start"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-10 mt-8 py-2 bg-[#130F49] text-gray-50 font-light rounded-md text-lg flex flex-row items-center"</span>&gt;</span>
          Send
          <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"24"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"24"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 24 24"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-cyan-500 ml-2"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M9.00967 5.12761H11.0097C12.1142 5.12761 13.468 5.89682 14.0335 6.8457L16.5089 11H21.0097C21.562 11 22.0097 11.4477 22.0097 12C22.0097 12.5523 21.562 13 21.0097 13H16.4138L13.9383 17.1543C13.3729 18.1032 12.0191 18.8724 10.9145 18.8724H8.91454L12.4138 13H5.42485L3.99036 15.4529H1.99036L4.00967 12L4.00967 11.967L2.00967 8.54712H4.00967L5.44417 11H12.5089L9.00967 5.12761Z"</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span> /&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
  );
}
</code></pre>
<p>Here we are calling the API we just created with <code>fetch</code>, which is provided by React.</p>
<p>Fetch calls the serverless API with the body like this:</p>
<pre><code class="lang-js">body: <span class="hljs-built_in">JSON</span>.stringify({
          <span class="hljs-attr">email</span>: email,
          <span class="hljs-attr">fullname</span>: fullname,
          <span class="hljs-attr">subject</span>: subject,
          <span class="hljs-attr">message</span>: message,
        })
</code></pre>
<p>These are our form fields with the form data filled in already (remember <code>useState()</code>?) which are now available to us.</p>
<p>The API responds with either success or failure. If it is a success, the email gets delivered – otherwise, the mail doesn't get delivered. </p>
<p>For the end-user to know the form status, we need to show some UI elements. But before that, we need to handle what happens if there are empty fields.</p>
<h2 id="heading-how-to-handle-form-validation-and-make-the-ui-respond-to-the-api-response">How to Handle Form Validation and Make the UI Respond to the API Response</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screenshot-2021-08-25-at-1.31.42-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>We need to ensure 3 things here:</p>
<ol>
<li>All the fields should be filled in – that is, we can't submit the form if any of the fields are empty. Also, the user must know why the form is not submitting. For that, we are going to display error messages.</li>
<li>While the form is being submitted, the user must know that some processing is going on. For that, we are going to change the button text when the form is in the process of submitting.</li>
<li>When the form is successfully submitted or if it fails, we are going to show the final status at the bottom of the form.</li>
</ol>
<p>Let's create a method <code>handleValidation()</code> to check for validation:</p>
<pre><code class="lang-js">
  <span class="hljs-keyword">const</span> handleValidation = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">let</span> tempErrors = {};
    <span class="hljs-keyword">let</span> isValid = <span class="hljs-literal">true</span>;

    <span class="hljs-keyword">if</span> (fullname.length &lt;= <span class="hljs-number">0</span>) {
      tempErrors[<span class="hljs-string">"fullname"</span>] = <span class="hljs-literal">true</span>;
      isValid = <span class="hljs-literal">false</span>;
    }
    <span class="hljs-keyword">if</span> (email.length &lt;= <span class="hljs-number">0</span>) {
      tempErrors[<span class="hljs-string">"email"</span>] = <span class="hljs-literal">true</span>;
      isValid = <span class="hljs-literal">false</span>;
    }
    <span class="hljs-keyword">if</span> (subject.length &lt;= <span class="hljs-number">0</span>) {
      tempErrors[<span class="hljs-string">"subject"</span>] = <span class="hljs-literal">true</span>;
      isValid = <span class="hljs-literal">false</span>;
    }
    <span class="hljs-keyword">if</span> (message.length &lt;= <span class="hljs-number">0</span>) {
      tempErrors[<span class="hljs-string">"message"</span>] = <span class="hljs-literal">true</span>;
      isValid = <span class="hljs-literal">false</span>;
    }

    setErrors({ ...tempErrors });
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"errors"</span>, errors);
    <span class="hljs-keyword">return</span> isValid;
  };
</code></pre>
<p>The function is pretty straightforward: it checks for all the fields and returns a boolean <code>isValid</code> if the form is valid. </p>
<p>Also, we are maintaining state for all the fields to display error messages at the end – essentially, we are saving the fields that contain errors.</p>
<p>The final code looks something like this, with button text, error messages, and form validations:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> React, { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ContactUs</span>(<span class="hljs-params"></span>) </span>{
   <span class="hljs-comment">// States for contact form fields</span>
  <span class="hljs-keyword">const</span> [fullname, setFullname] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [email, setEmail] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [subject, setSubject] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [message, setMessage] = useState(<span class="hljs-string">""</span>);

  <span class="hljs-comment">//   Form validation state</span>
  <span class="hljs-keyword">const</span> [errors, setErrors] = useState({});

  <span class="hljs-comment">//   Setting button text on form submission</span>
  <span class="hljs-keyword">const</span> [buttonText, setButtonText] = useState(<span class="hljs-string">"Send"</span>);

  <span class="hljs-comment">// Setting success or failure messages states</span>
  <span class="hljs-keyword">const</span> [showSuccessMessage, setShowSuccessMessage] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">const</span> [showFailureMessage, setShowFailureMessage] = useState(<span class="hljs-literal">false</span>);

  <span class="hljs-comment">// Validation check method</span>
  <span class="hljs-keyword">const</span> handleValidation = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">let</span> tempErrors = {};
    <span class="hljs-keyword">let</span> isValid = <span class="hljs-literal">true</span>;

    <span class="hljs-keyword">if</span> (fullname.length &lt;= <span class="hljs-number">0</span>) {
      tempErrors[<span class="hljs-string">"fullname"</span>] = <span class="hljs-literal">true</span>;
      isValid = <span class="hljs-literal">false</span>;
    }
    <span class="hljs-keyword">if</span> (email.length &lt;= <span class="hljs-number">0</span>) {
      tempErrors[<span class="hljs-string">"email"</span>] = <span class="hljs-literal">true</span>;
      isValid = <span class="hljs-literal">false</span>;
    }
    <span class="hljs-keyword">if</span> (subject.length &lt;= <span class="hljs-number">0</span>) {
      tempErrors[<span class="hljs-string">"subject"</span>] = <span class="hljs-literal">true</span>;
      isValid = <span class="hljs-literal">false</span>;
    }
    <span class="hljs-keyword">if</span> (message.length &lt;= <span class="hljs-number">0</span>) {
      tempErrors[<span class="hljs-string">"message"</span>] = <span class="hljs-literal">true</span>;
      isValid = <span class="hljs-literal">false</span>;
    }

    setErrors({ ...tempErrors });
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"errors"</span>, errors);
    <span class="hljs-keyword">return</span> isValid;
  };

  <span class="hljs-comment">//   Handling form submit</span>

  <span class="hljs-keyword">const</span> handleSubmit = <span class="hljs-keyword">async</span> (e) =&gt; {
    e.preventDefault();

    <span class="hljs-keyword">let</span> isValidForm = handleValidation();

    <span class="hljs-keyword">if</span> (isValidForm) {
      setButtonText(<span class="hljs-string">"Sending"</span>);
      <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"/api/sendgrid"</span>, {
        <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({
          <span class="hljs-attr">email</span>: email,
          <span class="hljs-attr">fullname</span>: fullname,
          <span class="hljs-attr">subject</span>: subject,
          <span class="hljs-attr">message</span>: message,
        }),
        <span class="hljs-attr">headers</span>: {
          <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
        },
        <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
      });

      <span class="hljs-keyword">const</span> { error } = <span class="hljs-keyword">await</span> res.json();
      <span class="hljs-keyword">if</span> (error) {
        <span class="hljs-built_in">console</span>.log(error);
        setShowSuccessMessage(<span class="hljs-literal">false</span>);
        setShowFailureMessage(<span class="hljs-literal">true</span>);
        setButtonText(<span class="hljs-string">"Send"</span>);
        <span class="hljs-keyword">return</span>;
      }
      setShowSuccessMessage(<span class="hljs-literal">true</span>);
      setShowFailureMessage(<span class="hljs-literal">false</span>);
      setButtonText(<span class="hljs-string">"Send"</span>);
    }
    <span class="hljs-built_in">console</span>.log(fullname, email, subject, message);
  };
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>
      // Rest of the JSX code goes here. (With form fields)
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
  );
}
</code></pre>
<p>When the form is delivered successfully, we get a nice response at the UI. To deliver that response, we have <code>showSuccessMessage</code> and <code>showFailureMessage</code> states. If the response from the back-end API route does NOT contain the property <code>error</code>, that means the form submission was successful and the email has been sent.</p>
<p>In that case, <code>showSuccessMessage</code> is set to True, which shows the corresponding markup just beneath the Form box. If the response body contains the property of <code>error</code>, the <code>showFailureMessage</code> is set to True and the corresponding message is displayed on the screen.</p>
<p>In both success and failure scenarios, we have to reset the button text to <code>send</code> instead of <code>sending...</code>. For that, we are using the state <code>setButtonText('send')</code> that sets the button text in case of failure or success. We set the button text to <code>sending...</code> when the Send button is clicked.</p>
<h2 id="heading-how-to-receive-emails-and-ui-responses">How to Receive Emails and UI Responses</h2>
<p>When the email is successfully delivered, we get a success message in the contact form itself.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screenshot-2021-08-25-at-1.37.32-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>And you will successfully receive an email with the template that we just created, securely delivered by SendGrid 💯</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screenshot-2021-08-25-at-1.38.54-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-environment-variables">Environment Variables</h2>
<p>Please note that we are using the API keys and the keys are sensitive. This means that we should always store secret or API keys in environment variables. </p>
<p>As we already have <code>.env.local</code> for our local environment, the hosting provider needs to know about the API keys too.</p>
<p>Vercel provides an easy way to store API keys on the hosting panel itself.</p>
<p>To store the API keys securely in your Vercel account, do the following:</p>
<ul>
<li>Go to your projects page</li>
<li>Go to settings</li>
<li>Go to Environment variables</li>
<li>Add the name of the environment variable, in our case it is <code>SENDGRID_API_KEY</code>, and add the corresponding API key in the value field.</li>
<li>Re-deploy your application and your project will work in a production environment.</li>
</ul>
<h2 id="heading-live-demo-and-source-code">Live Demo and Source Code</h2>
<p>Here are the source code and a live demo of the application:</p>
<p><a target="_blank" href="https://sendgrid-contact-form.vercel.app/">Live Demo</a>
<a target="_blank" href="https://github.com/manuarora700/sendgrid-contact-form">Source Code</a></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>SendGrid is an excellent option to use to send emails from a website. When you integrate it with Next.js and their serverless API routes, it becomes extremely easy to integrate forms in any part of your website. </p>
<p>SendGrid also gives you an option to integrate templates where you can have custom themes for your emails.</p>
<p>There are other options too for sending emails like <a target="_blank" href="https://nodemailer.com/about/">Nodemailer</a> that I've used in the past and still use for some of my projects.</p>
<p>It took me around an hour to build this application from the ground up – all thanks to Next.js, TailwindCSS, and SendGrid for their extremely intuitive workflow and API semantics. Also thanks to the <a target="_blank" href="https://tailwindmasterkit.com">Tailwind Master Kit</a> for the beautiful Contact Page UI.</p>
<p>If you liked this blog, try implementing it in your own website so you can reach out to your end-users. </p>
<p>If you'd like to give any feedback, reach out to me at my <a target="_blank" href="https://twitter.com/mannupaaji">Twitter handle</a> or visit my <a target="_blank" href="https://manuarora.in">Website</a></p>
<p>Happy coding. :)</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Send an Email Newsletter with the SendGrid API ]]>
                </title>
                <description>
                    <![CDATA[ For years, Quincy Larson sent a weekly email newsletter through freeCodeCamp's Mail for Good platform, which is powered by Amazon SES.  He recently migrated this process to SendGrid. In this article, I will show you how I built a tool to accomplish t... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/send-email-newsletter-with-the-sendgrid-api/</link>
                <guid isPermaLink="false">66ac7f5923cc28a03a55e08c</guid>
                
                    <category>
                        <![CDATA[ email ]]>
                    </category>
                
                    <category>
                        <![CDATA[ node js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ sendgrid ]]>
                    </category>
                
                    <category>
                        <![CDATA[ TypeScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Naomi Carrigan ]]>
                </dc:creator>
                <pubDate>Mon, 14 Dec 2020 18:07:58 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5fd3a6ebe6787e098393da0c.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>For years, Quincy Larson sent a weekly email newsletter through freeCodeCamp's <a target="_blank" href="https://github.com/freeCodeCamp/mail-for-good">Mail for Good</a> platform, which is powered by Amazon SES. </p>
<p>He recently migrated this process to <a target="_blank" href="https://sendgrid.com">SendGrid</a>. In this article, I will show you how I built a tool to accomplish this.</p>
<h2 id="heading-how-to-set-up-a-sendgrid-account">How to Set Up a SendGrid Account</h2>
<p>The first step is to <a target="_blank" href="https://signup.sendgrid.com/">register for SendGrid</a> and set up your account. For the purposes of this tutorial, the free tier should be sufficient.</p>
<p>As you scale up your application, you may need to increase your available email limits through the platform.</p>
<h2 id="heading-how-to-set-up-a-dedicated-ip-address-on-sendgrid">How to Set Up a Dedicated IP Address on SendGrid</h2>
<p>By default, SendGrid uses shared IP addresses for sending emails. This might be acceptable for smaller-scale email applications, but as you increase your send rates you will need to set up a dedicated IP address. </p>
<p>This is a good idea, because your "sender reputation" (the metric SendGrid uses to assess your standing with email service providers) will not be negatively impacted by the actions of other users who share the same IP.</p>
<p>To set up your own dedicated IP, select the "Settings" option from the side navigation menu, then select "IP Addresses". Just a quick note, however: this option is unavailable on the free tier. </p>
<p>Depending on your paid plan, you may already have one dedicated IP address set up. If you do not have one, or if you choose to add more, you can select the "Add an IP address" button to configure a new IP.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-87.png" alt="Image" width="600" height="400" loading="lazy">
<em>Setting menu for IP addresses</em></p>
<h2 id="heading-how-to-authorize-an-email-sender-in-sendgrid">How to Authorize an Email Sender in SendGrid</h2>
<p><em>NOTE: you can skip this section if you are using a custom domain for your emails.</em></p>
<p>To send emails from your personal email address, you will need to verify that the email address belongs to you.</p>
<p>In the left menu, select "Settings", then "Sender Authentication". Choose "Verify a Single Sender" to walk through the flow of adding your one email address.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-98.png" alt="Image" width="600" height="400" loading="lazy">
<em>Single Sender option</em></p>
<h2 id="heading-how-to-authenticate-your-custom-domain-in-sendgrid">How to Authenticate Your Custom Domain in SendGrid</h2>
<p><em>NOTE: you can skip this section if you are NOT using a custom domain for your emails.</em></p>
<p>In order to send emails from your custom mail domain, you will need to authenticate that domain with SendGrid. To get to this screen, select the Settings menu again, then select "Sender Authentication".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-88.png" alt="Image" width="600" height="400" loading="lazy">
<em>Setting menu for Sender Authentication</em></p>
<p>You should then see a screen with an option for "Domain Authentication". Select the "Authenticate Your Domain" option and SendGrid will walk you through the process for configuring your DNS records (with specific instructions based on your DNS provider). </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-91.png" alt="Image" width="600" height="400" loading="lazy">
<em>Sender Authenication settings page</em></p>
<h2 id="heading-how-to-set-up-reverse-dns-in-sendgrid">How to Set Up Reverse DNS in SendGrid</h2>
<p><em>NOTE: you can skip this section if you are not using a custom domain for your emails.</em></p>
<p>Reverse DNS (Domain Name System) is used by email providers to look up the owner of a given IP address. Setting this up will allow email providers to verify that the IP address you send an email from is connected to your custom domain.</p>
<p>In the same Sender Authentication screen as above, you will see a "Reverse DNS" section. There will be an option to configure reverse DNS for each dedicated IP you have on your account - like Domain Authentication, SendGrid's platform will walk you through setting this up.</p>
<h2 id="heading-how-to-set-up-email-authentication-in-sendgrid">How to Set Up Email Authentication in SendGrid</h2>
<p><em>NOTE: you can skip this section if you are not using a custom domain for your emails.</em></p>
<p>Major email providers (such as Gmail, Yahoo, and Outlook) use several methods for authenticating the sender of an email: SPF, DKIM, and DMARC.</p>
<ul>
<li><strong>SPF</strong> (Sender Policy Framework) validates that the IP address sending mail from your domain is authorized to do so.</li>
<li><strong>DKIM</strong> (DomainKeys Identified Mail) uses public key strings to authenticate that an email <code>from</code> address is accurate and not spoofed/falsified.</li>
<li><strong>DMARC</strong> (Domain-based Message Authentication, Reporting, and Conformance) is a set of instructions that tell email providers how to react when an email fails the SPF or DKIM validations.</li>
</ul>
<p>SendGrid's authentication flow will walk you through setting up SPF and DKIM as part of the domain authentication process, but you will need to configure your DMARC manually. </p>
<p>Visit your DNS hosting provider and access the DNS management settings. From there, add a new <code>TXT</code> record with a name of <code>_dmarc.yourdomain.com</code> (replacing <code>yourdomain.com</code> with your custom domain). </p>
<p>Note that some providers, such as GoDaddy, will automatically append your domain to the record - in this case, the name should be <code>_dmarc</code>. </p>
<p>The <em>value</em> of this record should take a similar structure to:</p>
<pre><code class="lang-text">"v=DMARC1; p=none; pct=100; rua=mailto:dmarc@yourdomain.com"
</code></pre>
<ul>
<li><code>v=DMARC</code> indicates the version of DMARC rules to use (currently only Version 1 is available).</li>
<li><code>p=none</code> indicates the action an email provider should take when an email fails DKIM or SPF. This setting should start as <code>none</code>, to avoid impacting deliverability of your emails. Once you have confirmed that your DKIM and SPF are configured correctly, you can update this value to <code>quarantine</code> to have providers automatically route failing emails to the spam folder, or <code>reject</code> to have providers reject/bounce failing emails.</li>
<li><code>pct=100</code> indicates the percentage of failing emails to which the action should be applied.</li>
<li><code>rua=mailto:dmarc@yourdomain.com</code> is the email address to send aggregate reports to. These reports contain information on all emails from your IPs that were received by a given provider. Replace the <code>dmarc@yourdomain.com</code> with the email address you want to receive those reports.</li>
</ul>
<h2 id="heading-how-to-create-a-dynamic-template-in-sendgrid">How to Create a Dynamic Template in SendGrid</h2>
<p>The tool we will be building today uses SendGrid's dynamic template feature to set the subject and body text of an email. To set this up, select the "Email API" option in the side navigation menu, then choose "Dynamic Templates".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-92.png" alt="Image" width="600" height="400" loading="lazy">
<em>Setting menu for Dynamic Templates</em></p>
<p>You will see a screen with a prompt to create your first dynamic template. Select the "Create a Dynamic Template" option. </p>
<p>Give your new template a name: "freeCodeCamp SendGrid Tutorial". SendGrid will add this template to a list of available templates. Select the template to see the <code>Template ID</code> (make a note of this, as we will need it for the tool later on) and click the "Add Version" button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-93.png" alt="Image" width="600" height="400" loading="lazy">
<em>Preview of the newly-added template</em></p>
<p>Select "Blank Template" on the screen that appears, then choose "Code Editor". You should now see the editor view. SendGrid's editor uses HTML to build the email body - however, when we build our tool we will be sending the plain text version. </p>
<p>For now, replace the contents of the editor with the following code:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>This is a test email used with the freeCodeCamp SendGrid tutorial<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Unsubscribe: {{{unsubscribeId}}}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>You will notice that we have added <code>{{{unsubscribeId}}}</code>. SendGrid's template uses Handlebars to dynamically replace values - we will be leveraging this feature when we build the tool. </p>
<p>Now select the settings option from the top left - you may optionally give your template version a name, but the "Subject" field is what we want to modify. Set this value to <code>{{{subject}}}</code> to dynamically load the subject value from our tool.</p>
<p>To test the dynamic template, select the "Test Data" option from the top menu. Insert this JSON data into the editor there:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"unsubscribeId"</span>: <span class="hljs-string">"1"</span>,
    <span class="hljs-attr">"subject"</span>: <span class="hljs-string">"Testing emails!"</span>
}
</code></pre>
<p>You should now see the preview on the right side of the screen reflect these values in the template. Remember to hit the <code>Save</code> button to save your changes!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-94.png" alt="Image" width="600" height="400" loading="lazy">
<em>Editor and Preview screen showing the dynamic loading of template values</em></p>
<h2 id="heading-how-to-generate-a-api-key-in-sendgrid">How to Generate a API Key in SendGrid</h2>
<p>The final step in configuring your SendGrid account is to generate an API key for our tool to use. </p>
<p>Click the back arrow at the top left to return to the main SendGrid page. Then select "Settings" and "API keys". Choose "Create API Key" to generate a new key. You may optionally grant "Full Access" to your key, but for the purpose of this tutorial you will only need "Mail Send" access. </p>
<p>Be sure to give your key a descriptive name so you will remember its purpose if you access this screen again. Once you have your permissions configured, select "Create and View" to generate the key - <strong>save this somewhere safe as you will not be able to view it again</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-95.png" alt="Image" width="600" height="400" loading="lazy">
<em>Create API screen with the Mail Send permission enabled</em></p>
<h2 id="heading-how-to-build-the-email-tool">How to Build the Email Tool</h2>
<p>Now it is time to write the code to actually send some emails. You can <a target="_blank" href="https://github.com/nhcarrigan/sendgrid-email-blast">view the code for our live application</a>, but for the purposes of this tutorial we will be building a <a target="_blank" href="https://github.com/nhcarrigan/fcc-sendgrid-tutorial">slightly reduced version</a> to focus primarily on using the SendGrid API.</p>
<h3 id="heading-required-software-for-a-custom-email-campaign-script">Required Software for a Custom Email Campaign Script</h3>
<p>You will need to have the following tools installed to work with this project:</p>
<ul>
<li><a target="_blank" href="https://nodejs.org/en/">Node.js</a> - the LTS version is recommended</li>
<li>An IDE, such as <a target="_blank" href="https://code.visualstudio.com">VSCode</a> or <a target="_blank" href="https://atom.io/">Atom</a></li>
</ul>
<p>You may also optionally want <code>git</code> for version control. </p>
<p>Our live tool uses a MongoDB Atlas cluster, but our tutorial example will not. If you are not familiar with MongoDB, freeCodeCamp's curriculum includes <a target="_blank" href="https://www.freecodecamp.org/learn/apis-and-microservices/mongodb-and-mongoose/">an excellent section on setting up and using MongoDB</a>.</p>
<h3 id="heading-how-to-initialize-the-project">How to Initialize the Project</h3>
<p>Create a directory (folder) in which to work on this project. Then open that folder with your editor and terminal of choice.</p>
<p>To get started, we will need to set this up as a Node project. The quickest way to do so is with <code>npm init</code> in your terminal. This will walk you through creating a <code>package.json</code> which is the core file of a Node application. </p>
<p>The default values will work fine for our application, but we will want to modify the <code>scripts</code> section:</p>
<pre><code class="lang-json">  <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"tsc"</span>,
    <span class="hljs-attr">"send"</span>: <span class="hljs-string">"node ./prod/send.js"</span>
  },
</code></pre>
<p>The <code>build</code> script will be used to compile our TypeScript into JavaScript, and the <code>send</code> script will run our application.</p>
<p>Next we will install and set up <a target="_blank" href="https://www.typescriptlang.org/">TypeScript</a>. If you are not familiar with TypeScript, it is essentially a superset of JavaScript with stronger type definitions and compile-time error checking. </p>
<p>To install TypeScript on your project, run <code>npm install --save-dev typescript</code> in your terminal. (The <code>--save-dev</code> flag saves it as a development dependency - TypeScript is not required at runtime so can be cleaned up in a production environment). </p>
<p>TypeScript requires its own configuration file to set the rules it should follow when generating the JavaScript files. Create a file in your project's root directory called <code>tsconfig.json</code> and insert the following:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"compilerOptions"</span>: {
      <span class="hljs-attr">"target"</span>: <span class="hljs-string">"es5"</span>,
      <span class="hljs-attr">"module"</span>: <span class="hljs-string">"commonjs"</span>,
      <span class="hljs-attr">"strict"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">"esModuleInterop"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">"skipLibCheck"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">"forceConsistentCasingInFileNames"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">"outDir"</span>: <span class="hljs-string">"./prod"</span>,
      <span class="hljs-attr">"rootDir"</span>: <span class="hljs-string">"./src"</span>
    }
  }
</code></pre>
<p>For the sake of brevity, we will not dive in to these configuration settings. If you would like additional information, TypeScript has <a target="_blank" href="https://www.typescriptlang.org/docs/">very in-depth documentation</a>.</p>
<p>If you are using <code>git</code> for version control and uploading this to a repository (such as GitHub), you will want to create a <code>.gitignore</code> file in your project's root directory. This file should contain:</p>
<pre><code class="lang-txt">/node_modules/
.env
/prod/
</code></pre>
<ul>
<li><code>/node_modules/</code> will ignore the installed packages. This is considered a best practise when working with version control.</li>
<li><code>.env</code> will ignore our environment variables file. This is very important as you <em>never</em> want to commit your secrets to a repository.</li>
<li><code>/prod/</code> will ignore our compiled JavaScript files. We will also use this folder for our email lists, so it is important to avoid accidentally committing that private identifiable information.</li>
</ul>
<p>Create a <code>.env</code> file in your root project directory. We will be loading the following environment variables through this file:</p>
<pre><code class="lang-txt">SENDGRID_API_KEY=
SENDGRID_FROM=
SENDGRID_TEMPLATE_ID=

MAIL_SUBJECT=
</code></pre>
<ul>
<li><code>SENDGRID_API_KEY</code> should be the API key you generated in the previous steps.</li>
<li><code>SENDGRID_FROM</code> should be your email address (this is the address used for the <code>from</code> field).</li>
<li><code>SENDGRID_TEMPLATE_ID</code> should be the <code>id</code> string for the dynamic template you created earlier.</li>
<li><code>MAIL_SUBJECT</code> will be the subject line for the emails you send. For now, set this as "fCC Tutorial Email".</li>
</ul>
<p>Finally, create a <code>src</code> folder in your root project directory, and create a <code>send.ts</code> file in that folder.</p>
<h3 id="heading-how-to-install-your-dependencies">How to Install Your Dependencies</h3>
<p>First we need to install the <code>sendgrid</code> Node.js package. This package serves as a wrapper for the SendGrid API and will streamline our process for making API calls to send emails. Run <code>npm install @sendgrid/mail</code> to install this package.</p>
<p>Then we need a couple of development dependencies. Run <code>npm install --save-dev dotenv @types/node</code>.</p>
<ul>
<li><code>dotenv</code> will allow us to load environment variables from the <code>.env</code> file locally.</li>
<li><code>@types/node</code> provides type definitions for Node.js - TypeScript relies on these definitions to understand the structure of the built-in methods and functions.</li>
</ul>
<h3 id="heading-how-to-write-the-logic">How to Write the Logic</h3>
<p>Now we will be working in our <code>/src/send.ts</code> file - this is where we are building the bulk of our app logic. We will start by importing the required values from our packages.</p>
<p>First we want to load the <code>dotenv</code> package and parse our environment variables.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> dotenv <span class="hljs-keyword">from</span> <span class="hljs-string">"dotenv"</span>;
dotenv.config();
</code></pre>
<p>The <code>dotenv.config()</code> call reads our <code>.env</code> file and loads the values into the <code>process.env</code> Node object.</p>
<p>Next we import the required modules from the SendGrid package:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> sgMail, { MailDataRequired } <span class="hljs-keyword">from</span> <span class="hljs-string">"@sendgrid/mail"</span>;
</code></pre>
<p><code>sgMail</code> is the primary API wrapper, and <code>MailDataRequired</code> is a type definition we will need.</p>
<p>Finally, we import some built-in Node features for handling our files:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> path <span class="hljs-keyword">from</span> <span class="hljs-string">"path"</span>;
<span class="hljs-keyword">import</span> { createWriteStream, readFile } <span class="hljs-keyword">from</span> <span class="hljs-string">"fs"</span>;
</code></pre>
<ul>
<li><code>path</code> will be used to locate our email list files with relative paths</li>
<li><code>fs</code> will be used to read and write to those files</li>
</ul>
<p>Time to start building the logic! Our application relies on some essential values to be set in the <code>.env</code> file, so we need to start by validating that those variables are set correctly. If any are missing, we want our application to exit early to avoid throwing errors when we send the emails.</p>
<pre><code class="lang-ts"><span class="hljs-comment">// Here we check for a valid API key</span>
<span class="hljs-keyword">const</span> apiKey = process.env.SENDGRID_API_KEY;
<span class="hljs-keyword">if</span> (!apiKey) {
  <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Missing SendGrid Key"</span>);
  process.exit(<span class="hljs-number">1</span>);
}

<span class="hljs-comment">// Here we check for a valid from address</span>
<span class="hljs-keyword">const</span> fromAddress = process.env.SENDGRID_FROM;
<span class="hljs-keyword">if</span> (!fromAddress) {
  <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Missing sender email address!"</span>);
  process.exit(<span class="hljs-number">1</span>);
}

<span class="hljs-comment">// Here we check for a dynamic template ID</span>
<span class="hljs-keyword">const</span> sgTemplate = process.env.SENDGRID_TEMPLATE_ID;
<span class="hljs-keyword">if</span> (!sgTemplate) {
  <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Missing SendGrid Template ID"</span>);
  process.exit(<span class="hljs-number">1</span>);
}

<span class="hljs-comment">// Here we check for the mail subject, but if it is missing</span>
<span class="hljs-comment">// we do not need to exit. Instead we use a fallback value.</span>
<span class="hljs-keyword">const</span> subjectValue = process.env.MAIL_SUBJECT || <span class="hljs-string">"Fallback value - check your env!"</span>;
</code></pre>
<p>The <code>process.exit(1)</code> call you see in each condition check tells Node to terminate the process (our application) with an exit code of <code>1</code>. This indicates that our application crashed due to one of these checks failing.</p>
<p>SendGrid requires us to set the API key. Below your environment variable logic, add the function call to set the key.</p>
<pre><code class="lang-ts"><span class="hljs-comment">// Here we set the SendGrid API key</span>
sgMail.setApiKey(apiKey);
</code></pre>
<p>Before moving forward, go ahead and run <code>npm run build</code> in your terminal - this will create a <code>prod</code> folder containing our compiled JavaScript. You should now see the following file structure:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-96.png" alt="Image" width="600" height="400" loading="lazy">
<em>File Tree for this point in the tutorial</em></p>
<p><strong>At this point, if you are using <code>git</code> you want to be <em>very sure</em> that the <code>prod</code> folder will not be committed to your repository.</strong> </p>
<p>Within the <code>prod</code> folder, create a <code>validEmails.csv</code> file. Our app will use this file to read the email list. Initialise the file with the following contents (replace <code>your@email.com</code> with your actual email address):</p>
<pre><code class="lang-csv">email,unsubscribeId
your@email.com,1
iama@fake.email,2
</code></pre>
<p>Now we can write the code to parse this into an email list! In your <code>src/send.ts</code> file, add this code:</p>
<pre><code><span class="hljs-comment">// Here we concatenate our file path for the valid email file</span>
<span class="hljs-keyword">const</span> filePath = path.join(__dirname + <span class="hljs-string">"/../validEmails.csv"</span>);

<span class="hljs-comment">// This is where we start reading the file!</span>
readFile(filePath, <span class="hljs-string">"utf8"</span>, <span class="hljs-function">(<span class="hljs-params">err, data</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (err) {
        <span class="hljs-built_in">console</span>.error(err);
        <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-built_in">console</span>.log(data)
});
</code></pre><p>Now if you run <code>npm run build</code> and <code>npm run send</code> you should see the contents of our <code>validEmail.csv</code> file in the terminal. If you would like, you can <a target="_blank" href="https://github.com/naomis-archive/fcc-sendgrid-tutorial/blob/main/tutorial-steps/step-1.ts">view our current progress up to this point</a>. </p>
<p>Great! Now we need to parse that string into an array of objects so we can iterate through it and build our email messages. Update our callback function:</p>
<pre><code><span class="hljs-comment">// This is where we start reading the file!</span>
readFile(filePath, <span class="hljs-string">"utf8"</span>, <span class="hljs-function">(<span class="hljs-params">err, data</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (err) {
        <span class="hljs-built_in">console</span>.error(err);
        <span class="hljs-keyword">return</span>;
    }

  <span class="hljs-comment">// Here we parse the data into an object array</span>
  <span class="hljs-keyword">const</span> emailList = data
    .split(<span class="hljs-string">"\n"</span>)
    .slice(<span class="hljs-number">1</span>)
    .map(<span class="hljs-function">(<span class="hljs-params">el</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> [email, unsubscribeId] = el.split(<span class="hljs-string">","</span>);
      <span class="hljs-keyword">return</span> { email, unsubscribeId };
    });
});
</code></pre><ul>
<li><code>.split("\n")</code> splits the string by line breaks. <strong>NOTE</strong>: If you are on Windows, you may need to change the end-of-line setting for your <code>validEmails.csv</code> from <code>CRLF</code> to <code>LF</code> (Windows inserts extra linebreak characters that will impact our data handling)</li>
<li><code>.slice(1)</code> removes the first element of that array (our <code>email,unsubscribeId</code> line).</li>
<li>Our <code>map</code> function will convert each <code>email,unsubscribeId</code> string into an <code>{email, unsubscribeId}</code> object.</li>
</ul>
<p>The end result of this parsing function will be an array of objects with <code>email</code> and <code>unsubscribeId</code> properties - much smoother to work with than a string.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-97.png" alt="Image" width="600" height="400" loading="lazy">
<em>Example output from parsing function</em></p>
<p>It is now time to send some emails. Below your parsing function (but still within the <code>readFile</code> callback) add the structure for our iteration method. Because we want to access each value in the array, we will use the <code>.forEach</code> approach.</p>
<pre><code class="lang-ts">  <span class="hljs-comment">// Here we iterate through the emailList array</span>
  emailList.forEach(<span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> {});
</code></pre>
<p>Within the callback for the <code>.forEach</code>, we can construct the message object the SendGrid API expects. </p>
<pre><code class="lang-ts">  <span class="hljs-comment">// Here we iterate through the emailList array</span>
  emailList.forEach(<span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> {

    <span class="hljs-comment">// This is the message object SendGrid needs</span>
    <span class="hljs-keyword">const</span> message: MailDataRequired = {
        to: user.email,
        <span class="hljs-keyword">from</span>: fromAddress,
        subject: subjectValue,
        text: <span class="hljs-string">"This goes away!"</span>,
        templateId: sgTemplate,
        dynamicTemplateData: {
            subject: subjectValue,
            unsubscribeId: user.unsubscribeId
        }
    }

  });
</code></pre>
<p>Before moving on, let's look more closely at this message object. The <code>MailDataRequired</code> we imported earlier is used as the type definition here, so TypeScript can alert us if we happen to miss a required property. Thankfully, we have all of the required properties. But what do they mean?</p>
<ul>
<li><code>to:</code> The email address to send the message to. This will be the <code>email</code> from each line of our <code>validEmails.csv</code> file</li>
<li><code>from:</code> The email address to send the message from. This is set in our <code>.env</code> earlier (it should be <em>your</em> email address).</li>
<li><code>subject:</code> This field is not required, but gives us a fallback value in case the dynamic template doesn't parse our subject correctly.</li>
<li><code>text:</code> This text value is overwritten by the template. However, it is still important to use. SendGrid can send emails as <code>plaintext</code> or <code>html</code> - by using the <code>text</code> property instead of the <code>html</code> property, we ensure our template is sent as <code>plaintext</code>. Email providers are <em>more likely</em> to flag HTML messages as spam, so this helps increase our deliverability rate.</li>
<li><code>templateId:</code> This is the ID for the dynamic template SendGrid should use in the email.</li>
<li><code>dynamicTemplateData:</code> These are the values that correspond with our Handlebars strings we set in the dynamic template earlier. </li>
</ul>
<p>Great! Our next step is to take this constructed message and send it. Below the message object (but still within the <code>.forEach</code> callback), let's add our send call:</p>
<pre><code class="lang-ts">    <span class="hljs-comment">// Here we send the message we just constructed!</span>
    sgMail.send(message);
</code></pre>
<p>This will send the message to each of the emails in our <code>validEmails.csv</code>. Unfortunately, our code will run silently and we will not know if each send was successful or not. Let's add some error handling. </p>
<p>The <code>.send()</code> call returns a Promise, so we can use <code>.then().catch()</code> to handle the return.</p>
<pre><code class="lang-ts">    <span class="hljs-comment">// Here we send the message we just constructed!</span>
    sgMail.send(message)
        .then(<span class="hljs-function">() =&gt;</span> {
            <span class="hljs-comment">// Here we log successful send requests</span>
            <span class="hljs-built_in">console</span>.info(<span class="hljs-string">`Message send success: <span class="hljs-subst">${user.email}</span>`</span>)
        }).catch(<span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {
            <span class="hljs-comment">// Here we log errored send requests</span>
            <span class="hljs-built_in">console</span>.error(err);
            <span class="hljs-built_in">console</span>.error(<span class="hljs-string">`Message send failed: <span class="hljs-subst">${user.email}</span>`</span>)
        });
</code></pre>
<p>Now if you run <code>npm run build</code> and <code>npm run send</code> you should see a beautiful email in your inbox!</p>
<p>At this point, you now have a functional email sending application. Congratulations! You can <a target="_blank" href="https://github.com/naomis-archive/fcc-sendgrid-tutorial/blob/main/tutorial-steps/step-2.ts">view our progress to this point</a> if you'd like. </p>
<p>Read on to see how to handle bounced emails and additional logic for send failures, which is what we'll discuss next.</p>
<h3 id="heading-how-to-handle-bounced-emails-in-sendgrid">How to Handle Bounced Emails in SendGrid</h3>
<p>You may have noticed that <code>iama@fake.email</code> is very much not a real email address. SendGrid will generate bounced reports daily for your activity the previous day. </p>
<p>Every bounced email hurts your SendGrid reputation and can lead to email providers marking your mail as spam. So, we need to add logic to prevent sending to known bouncing addresses.</p>
<p>Start by creating a <code>bouncedEmails.csv</code> file in your <code>prod</code> folder (it should be next to your <code>validEmails.csv</code>). We do not need <code>unsubscribeId</code> values here, so initialise it with:</p>
<pre><code class="lang-csv">email
iama@fake.email
</code></pre>
<p>Now back to our <code>send.ts</code> file. On line 38, just below our existing <code>filePath</code> declaration, configure the path for the new <code>bouncedEmails.csv</code> file.</p>
<pre><code><span class="hljs-comment">// Here we concatenate our file paths for the CSV files</span>
<span class="hljs-keyword">const</span> filePath = path.join(__dirname + <span class="hljs-string">"/validEmails.csv"</span>);
<span class="hljs-keyword">const</span> bouncePath = path.join(__dirname + <span class="hljs-string">"/bouncedEmails.csv"</span>);
</code></pre><p>Great! Now we need to read that file. Immediately below these file path declarations (before our existing <code>readFile</code> call), add the logic for reading the bounced files.</p>
<pre><code><span class="hljs-comment">// Read through the bounce list, parse into array</span>
readFile(bouncePath, <span class="hljs-string">"utf8"</span>, <span class="hljs-function">(<span class="hljs-params">err, data</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (err) {
    <span class="hljs-built_in">console</span>.error(err);
    process.exit(<span class="hljs-number">1</span>);
  }
  bounceList = data.split(<span class="hljs-string">"\n"</span>).slice(<span class="hljs-number">1</span>);
</code></pre><p><code>readFile</code> is asynchronous - so we need to wrap the callback function around <em>all of our existing send logic</em>. Make sure that your closing <code>})</code> for this callback is moved to the very end of our file.</p>
<p>We read the <code>bouncedEmails.csv</code> file, split it on the new line (remember that you will need to ensure your line endings are <code>LF</code>), and remove the <code>email</code> line. Finally, we continue with our existing send logic.</p>
<p>Back to our send logic. Within our <code>.forEach</code> function, add logic to skip blocked emails (we will add this before constructing the message object to avoid creating unnecessary variables).</p>
<pre><code class="lang-ts">  <span class="hljs-comment">// Here we iterate through the emailList array</span>
  emailList.forEach(<span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> {
    <span class="hljs-comment">// Here we check if the email has been bounced</span>
    <span class="hljs-keyword">if</span> (bounceList.length &amp;&amp; bounceList.includes(user.email)) {
        <span class="hljs-built_in">console</span>.info(<span class="hljs-string">`Message send skipped: <span class="hljs-subst">${user.email}</span>`</span>);
        <span class="hljs-keyword">return</span>;
    }
</code></pre>
<p>By leveraging an early <code>return</code> statement, we end that particular <code>.forEach</code> iteration when the <code>bounceList</code> includes that email. This prevents us from attempting to send to email addresses that have previously bounced. Now if you run <code>npm run build</code> and <code>npm run start</code>, you should see this output in your terminal:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-99.png" alt="Image" width="600" height="400" loading="lazy">
<em>Example console output for a skipped email and successful email</em></p>
<p><a target="_blank" href="https://github.com/naomis-archive/fcc-sendgrid-tutorial/blob/main/tutorial-steps/step-3.ts">View our progress up to this point</a>.</p>
<h3 id="heading-how-to-capture-failed-emails-in-sendgrid">How to Capture Failed Emails in SendGrid</h3>
<p>Currently our application will log an error if an email fails to send. This can work for small use cases, but as you scale your application up you will find it increasingly difficult to identify those failures and attempt to send again. </p>
<p>But instead, we can make our application save those emails in a new file.</p>
<p>Create a <code>failedEmails.csv</code> file in your <code>prod</code> folder. This file can be empty. We will write the code to add the header row.</p>
<p>Back to our <code>send.ts</code> file, head to our path declarations on line 38. Let's add one more for our new <code>failedEmails.csv</code>:</p>
<pre><code class="lang-ts"><span class="hljs-comment">// Here we concatenate our file paths for the CSV files</span>
<span class="hljs-keyword">const</span> filePath = path.join(__dirname + <span class="hljs-string">"/validEmails.csv"</span>);
<span class="hljs-keyword">const</span> bouncePath = path.join(__dirname + <span class="hljs-string">"/bouncedEmails.csv"</span>);
<span class="hljs-keyword">const</span> failedPath = path.join(__dirname + <span class="hljs-string">"/failedEmails.csv"</span>);
</code></pre>
<p>Unlike our other paths, this path will be used for a <code>write</code> operation. Because we want to write continuously as emails are processed, we need to create a stream to do so. Just below these path declarations, let's create that stream and add our initial header row.</p>
<pre><code class="lang-ts"><span class="hljs-comment">// Here we create our write stream for failed emails</span>
<span class="hljs-keyword">const</span> failedStream = createWriteStream(failedPath);

<span class="hljs-comment">// Here we add the header row</span>
failedStream.write(<span class="hljs-string">"email,unsubscribeId\n"</span>)
</code></pre>
<p>Time to improve our error handling logic to incorporate this new stream. We need to add another <code>write</code> operation to our error handling in the <code>send</code> call.</p>
<pre><code class="lang-ts">    <span class="hljs-comment">// Here we send the message we just constructed!</span>
    sgMail
      .send(message)
      .then(<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-comment">// Here we log successful send requests</span>
        <span class="hljs-built_in">console</span>.info(<span class="hljs-string">`Message send success: <span class="hljs-subst">${user.email}</span>`</span>);
      })
      .catch(<span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {
        <span class="hljs-comment">// Here we log errored send requests</span>
        <span class="hljs-built_in">console</span>.error(err);
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">`Message send failed: <span class="hljs-subst">${user.email}</span>`</span>);
        <span class="hljs-comment">// And here we add that email to the failedEmails.csv</span>
        failedStream.write(<span class="hljs-string">`<span class="hljs-subst">${user.email}</span>,<span class="hljs-subst">${user.unsubscribeId}</span>\n`</span>)
      });
</code></pre>
<p>This will write the <code>email</code> and <code>unsubscribeId</code> to our new <code>failedEmails.csv</code> in the proper format - allowing us to copy that data into the <code>validEmails.csv</code> to make another send attempt.</p>
<p>Congratulations! You've now built a successful and fully functional tool to send out email blasts. You can <a target="_blank" href="https://github.com/naomis-archive/fcc-sendgrid-tutorial/blob/main/tutorial-steps/step-4.ts">see the completed code</a> if you want to confirm your work. But keep reading for a couple of optional, "nice to have" features.</p>
<h2 id="heading-optional-features-for-your-email-tool">Optional Features for Your Email Tool</h2>
<p>Because our tool is CLI-based (meaning it is used in the command-line interface, or terminal), there is not much user feedback. We can leverage some additional console functions to provide more information about the progress of the script.</p>
<p>Let's start by adding some "checkpoints". Before our environment variable validation, let's print a message that the script has started and is checking the variables:</p>
<pre><code class="lang-ts"><span class="hljs-built_in">console</span>.info(<span class="hljs-string">'Script started. Validating environment variables...'</span>)
</code></pre>
<p>Then, after our validation, we can print a success message.</p>
<pre><code class="lang-ts"><span class="hljs-comment">// Here we set the SendGrid API key</span>
sgMail.setApiKey(apiKey);

<span class="hljs-built_in">console</span>.info(<span class="hljs-string">'Variables confirmed!'</span>)
</code></pre>
<p>Within our function to read the bounced file, we can add some messages for start, failure, and success.</p>
<pre><code class="lang-ts"><span class="hljs-built_in">console</span>.info(<span class="hljs-string">'Reading bounced email list...'</span>)

<span class="hljs-comment">// Read through the bounce list, parse into array</span>
readFile(bouncePath, <span class="hljs-string">"utf8"</span>, <span class="hljs-function">(<span class="hljs-params">err, data</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (err) {
    <span class="hljs-built_in">console</span>.error(err);
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Failed to read bounced emails!'</span>)
    process.exit(<span class="hljs-number">1</span>);
  }
  bounceList = data.split(<span class="hljs-string">"\n"</span>).slice(<span class="hljs-number">1</span>);

<span class="hljs-built_in">console</span>.info(<span class="hljs-string">'Bounced emails read!'</span>)
</code></pre>
<p>And the same for our valid email list:</p>
<pre><code class="lang-ts"><span class="hljs-built_in">console</span>.info(<span class="hljs-string">'Reading send list...'</span>)
<span class="hljs-comment">// This is where we start reading the file!</span>
readFile(filePath, <span class="hljs-string">"utf8"</span>, <span class="hljs-function">(<span class="hljs-params">err, data</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (err) {
    <span class="hljs-built_in">console</span>.error(err);
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Failed to read send list!'</span>)
    <span class="hljs-keyword">return</span>;
  }
</code></pre>
<p>Now, it would be very nice to have a message print when the operation completes. However, if we add a <code>console.info</code> after our <code>.forEach</code> loop, it will actually print <em>before</em> the emails have finished sending!</p>
<p> This is because the <code>.send</code> method creates a network call and returns a Promise, and that Promise might not have resolved/rejected before our iteration has finished. </p>
<p>So instead we can build a counter to track the number of emails we have sent versus the total number of emails. Just before our <code>.forEach</code> loop, add these variables:</p>
<pre><code class="lang-ts">    <span class="hljs-comment">// Here we create variables for counting</span>
    <span class="hljs-keyword">const</span> emailTotal = emailList.length;
    <span class="hljs-keyword">let</span> emailCount = <span class="hljs-number">0</span>;
</code></pre>
<p>We want to count bounced emails as processed, even though we are skipping them.</p>
<pre><code class="lang-ts">  <span class="hljs-comment">// Here we iterate through the emailList array</span>
  emailList.forEach(<span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> {
    <span class="hljs-comment">// Here we check if the email has been bounced</span>
    <span class="hljs-keyword">if</span> (bounceList.includes(user.email)) {
      <span class="hljs-built_in">console</span>.info(<span class="hljs-string">`Message send skipped: <span class="hljs-subst">${user.email}</span>`</span>);
      emailCount++;
      <span class="hljs-keyword">if</span> (emailCount === emailTotal) {
        <span class="hljs-built_in">console</span>.info(
          <span class="hljs-string">`Sending complete! Sent <span class="hljs-subst">${emailTotal}</span> emails. Have a nice day!`</span>
        );
        <span class="hljs-keyword">return</span>;
      }
    }
</code></pre>
<p>Finally we need to add logic to see if the email we have sent is the last email. This logic goes in our success and error handlers for the send call:</p>
<pre><code>    <span class="hljs-comment">// Here we send the message we just constructed!</span>
    sgMail
      .send(message)
      .then(<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-comment">// Here we log successful send requests</span>
        <span class="hljs-built_in">console</span>.info(<span class="hljs-string">`Message send success: <span class="hljs-subst">${user.email}</span>`</span>);
        <span class="hljs-comment">// Here we handle the email counts</span>
        emailCount++;
        <span class="hljs-keyword">if</span> (emailCount === emailTotal) {
          <span class="hljs-built_in">console</span>.info(
            <span class="hljs-string">`Sending complete! Sent <span class="hljs-subst">${emailTotal}</span> emails. Have a nice day!`</span>
          );
        }
      })
      .catch(<span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {
        <span class="hljs-comment">// Here we log errored send requests</span>
        <span class="hljs-built_in">console</span>.error(err);
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">`Message send failed: <span class="hljs-subst">${user.email}</span>`</span>);
        <span class="hljs-comment">// And here we add that email to the failedEmails.csv</span>
        failedStream.write(<span class="hljs-string">`<span class="hljs-subst">${user.email}</span>,<span class="hljs-subst">${user.unsubscribeId}</span>\n`</span>);
        <span class="hljs-comment">// Here we handle the email counts</span>
        emailCount++;
        <span class="hljs-keyword">if</span> (emailCount === emailTotal) {
          <span class="hljs-built_in">console</span>.info(
            <span class="hljs-string">`Sending complete! Sent <span class="hljs-subst">${emailTotal}</span> emails. Have a nice day!`</span>
          );
        }
      });
</code></pre><p>And with that, our app is fully complete! If you run the <code>npm run build</code> and <code>npm run send</code> scripts, you should see this output in your terminal:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-100.png" alt="Image" width="600" height="400" loading="lazy">
<em>Example console output for completed application.</em></p>
<p>And you should have received a few emails that look similar to this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-101.png" alt="Image" width="600" height="400" loading="lazy">
<em>Sample image of test email result</em></p>
<p>You can <a target="_blank" href="https://github.com/nhcarrigan/fcc-sendgrid-tutorial/blob/main/tutorial-steps/step-5.ts">view our final code here</a>, or you can <a target="_blank" href="https://github.com/freecodecamp/sendgrid-email-blast">view the extended version</a> built for freeCodeCamp.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
