<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Jessica Chowdhury</title>
    <description>The latest articles on Forem by Jessica Chowdhury (@jesschow).</description>
    <link>https://forem.com/jesschow</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1051690%2F9a03d2a4-8470-400e-990d-f0f503aff201.jpg</url>
      <title>Forem: Jessica Chowdhury</title>
      <link>https://forem.com/jesschow</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jesschow"/>
    <language>en</language>
    <item>
      <title>Error Management Magic: Introducing the Sentry Plugin for Payload</title>
      <dc:creator>Jessica Chowdhury</dc:creator>
      <pubDate>Thu, 27 Jul 2023 13:38:15 +0000</pubDate>
      <link>https://forem.com/payloadcms/error-management-magic-introducing-the-sentry-plugin-for-payload-30b3</link>
      <guid>https://forem.com/payloadcms/error-management-magic-introducing-the-sentry-plugin-for-payload-30b3</guid>
      <description>&lt;p&gt;This integration brings Sentry's real-time error tracking, alerting, and performance monitoring features directly into your Payload project. We want to empower your developers to diagnose and resolve issues with unmatched efficiency.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;yarn&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;payloadcms&lt;/span&gt;&lt;span class="sr"&gt;/plugin-sentr&lt;/span&gt;&lt;span class="err"&gt;y
&lt;/span&gt;&lt;span class="c1"&gt;// OR&lt;/span&gt;
&lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;payloadcms&lt;/span&gt;&lt;span class="sr"&gt;/plugin-sentr&lt;/span&gt;&lt;span class="err"&gt;y
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What is error tracking and why does it matter?
&lt;/h2&gt;

&lt;p&gt;Error tracking is the process of meticulously collecting and analyzing errors that occur within your application. With a strong error tracking solution, you open the door to a plethora of benefits that will elevate the overall quality of your project.&lt;/p&gt;

&lt;p&gt;A good error tracking solution will allow you to...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prioritize and address errors ensuring that the most important errors are fixed first&lt;/li&gt;
&lt;li&gt;Identify recurring errors and patterns earlier and prevent them from snowballing&lt;/li&gt;
&lt;li&gt;Save valuable time when it comes to debugging and troubleshooting errors&lt;/li&gt;
&lt;li&gt;Increase application reliability and reduce downtime&lt;/li&gt;
&lt;li&gt;Improve the overall developer and user experience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ultimately, error tracking serves as a key component in driving the overall quality of your software. By addressing issues promptly, improving reliability, and optimizing the development process, you can deliver a product that takes software development to new heights.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sentry: The Error Monitoring Powerhouse
&lt;/h2&gt;

&lt;p&gt;Sentry is a &lt;a href="https://sentry.io/welcome/" rel="noopener noreferrer"&gt;leading, feature-rich, open-source platform&lt;/a&gt; for error and performance monitoring.  With minimal setup, you can start capturing valuable data from your application and access it through Sentry's comprehensive and easy to use dashboard.&lt;/p&gt;

&lt;p&gt;Here is a glance at their awesome features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Error tracking&lt;/strong&gt;: Get detailed reports with stack traces, environment data, and user info&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance monitoring&lt;/strong&gt;: Including user interactions, network requests, and other performance metrics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Release tracking and health monitoring&lt;/strong&gt;: Keep tabs on releases and monitor application health with ease&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-project visibility&lt;/strong&gt;: Sentry lets you manage and monitor errors across all multiple apps from one central platform&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom dashboards&lt;/strong&gt;: Access the metrics that matter most to you and personalize the dashboard to your needs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notifications and email alerts&lt;/strong&gt;: Add and customize alerts so you'll be the first to know when something is happening&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that's just the tip of the iceberg. Sentry's features empowers you to manage errors with extreme efficiency, saving time and resources while delivering a top user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-Step Plugin Integration Guide
&lt;/h2&gt;

&lt;p&gt;Let’s walkthrough the setup process for the Sentry-Payload plugin:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Installing the Plugin
&lt;/h4&gt;

&lt;p&gt;Navigate to your Payload app then install the plugin using Yarn or NPM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;yarn&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;payloadcms&lt;/span&gt;&lt;span class="sr"&gt;/plugin-sentr&lt;/span&gt;&lt;span class="err"&gt;y
&lt;/span&gt;&lt;span class="c1"&gt;// OR&lt;/span&gt;
&lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;payloadcms&lt;/span&gt;&lt;span class="sr"&gt;/plugin-sentr&lt;/span&gt;&lt;span class="err"&gt;y
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Creating a Sentry Account
&lt;/h4&gt;

&lt;p&gt;If you don't have a Sentry account already, it's time to create one. Head over to Sentry and create an account. The registration process is straightforward and will only take a few minutes.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Locating Your DSN
&lt;/h4&gt;

&lt;p&gt;The DSN (Data Source Name) is a unique identifier that connects your application to Sentry. After creating your Sentry account, you can find your project's DSN in the project settings or configuration section. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcbm4w7w1cju9k73bmpww.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcbm4w7w1cju9k73bmpww.png" alt="Find your DSN in the Sentry project settings" width="800" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Add DSN to your Payload Config file
&lt;/h4&gt;

&lt;p&gt;By this step, you should have the plugin installed and your DSN identifier ready. Navigate to your &lt;code&gt;payload.config()&lt;/code&gt; file and add the plugin along with your DSN:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;sentry&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;dsn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;your-dsn-string-here&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  5. Configure additional options (not required)
&lt;/h4&gt;

&lt;p&gt;These options allow you to fine-tune the Sentry integration to your specific needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;init&lt;/strong&gt; : ClientOptions | optional&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sentry allows a variety of options to be passed into the Sentry.init() function, see the full list of options &lt;a href="https://dev.tohere"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;requestHandler&lt;/strong&gt; : RequestHandlerOptions | optional&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Accepts options that let you decide what data should be included in the event sent to Sentry, checkout the options &lt;a href="https://docs.sentry.io/platforms/node/guides/express/configuration/options" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;captureErrors&lt;/strong&gt;: number[] | optional&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By default, Sentry.errorHandler will capture only errors with a status code of 500 or higher. To capture additional error codes, pass the values as numbers in an array.&lt;/p&gt;

&lt;p&gt;Pass any of these options to the plugin like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;sentry&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;dsn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;your-dsn-string-here&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;init&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;development&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;tracesSampleRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;requestHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;serverName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;captureErrors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Take it for a spin
&lt;/h2&gt;

&lt;p&gt;Once you have the plugin installed and configured, Sentry will be ready to start capturing errors for you. So let’s take this plugin for a test drive by intentionally triggering an error:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start your Payload app with &lt;code&gt;yarn dev&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to the login page in your browser&lt;/li&gt;
&lt;li&gt;Instead of using your valid credentials, enter an incorrect password&lt;/li&gt;
&lt;li&gt;This should throw the following error:  &lt;code&gt;AuthenticationError: The email or password provided is incorrect&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Go to &lt;a href="https://sentry.io/" rel="noopener noreferrer"&gt;sentry.io&lt;/a&gt; and select the project associated with the DSN in your &lt;code&gt;payload.config()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;See the new error in your dashboard, click into it for more detail&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F08lxj4x3d6qokp70rc37.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F08lxj4x3d6qokp70rc37.png" alt="Sample error shown in the Sentry dashboard" width="800" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap Up
&lt;/h2&gt;

&lt;p&gt;A robust error tracking solution is crucial to delivering a reliable and performant application. Sentry provides developers with the ultimate toolbox, comprehensive error and performance monitoring, custom dashboards, real-time notifications and much more. &lt;/p&gt;

&lt;p&gt;This plugin aims to make integrating Sentry into your Payload project a breeze. With 3 lines of code, you can instantly improve your error handling workflow. Give it a go and as always, reach out to us if you have any questions or feedback! Happy tracking 👋&lt;/p&gt;

</description>
      <category>sentry</category>
      <category>payload</category>
      <category>headless</category>
      <category>cms</category>
    </item>
    <item>
      <title>Payload + Nodemailer: Free and Extensible Email Integration</title>
      <dc:creator>Jessica Chowdhury</dc:creator>
      <pubDate>Fri, 26 May 2023 15:45:03 +0000</pubDate>
      <link>https://forem.com/payloadcms/payload-nodemailer-free-and-extensible-email-integration-20fc</link>
      <guid>https://forem.com/payloadcms/payload-nodemailer-free-and-extensible-email-integration-20fc</guid>
      <description>&lt;p&gt;Email is a necessary evil in our lives. We don’t love it, but we need it.&lt;/p&gt;

&lt;p&gt;With Payload, you don't have to abandon what you're already familiar with. Stick to your trusty SMTP if you like to keep things simple. Or, if you're a fan of &lt;a href="https://gmail.com/" rel="noopener noreferrer"&gt;Gmail&lt;/a&gt; or &lt;a href="https://outlook.com/" rel="noopener noreferrer"&gt;Outlook&lt;/a&gt;, go ahead and integrate them with ease. You can even bring in other powerful email service tools like &lt;a href="https://sendgrid.com/" rel="noopener noreferrer"&gt;SendGrid&lt;/a&gt;, &lt;a href="https://resend.com/" rel="noopener noreferrer"&gt;Resend&lt;/a&gt;, &lt;a href="https://www.hubspot.com/" rel="noopener noreferrer"&gt;HubSpot&lt;/a&gt; and more. &lt;/p&gt;

&lt;p&gt;Integrating email with Payload is free, flexible and highly extensible. No matter what kind of emails you need to send – from newsletters, transactional and marketing emails, to those crucial authentication emails – we've got you covered.&lt;/p&gt;

&lt;p&gt;In this post, we’ll walk through the process of configuring email with Payload and cover everything you need to get up and running. Let’s dive in!&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;p&gt;Payload utilizes Nodemailer to produce a versatile email transporter which can then be used anywhere in your application.&lt;/p&gt;

&lt;p&gt;For those who are new to Nodemailer, it is a powerful module in the Node.js ecosystem that greatly simplifies the process of sending email. We recommend taking a look at the  &lt;a href="https://nodemailer.com/about/" rel="noopener noreferrer"&gt;Nodemailer docs&lt;/a&gt; if you want to learn more.&lt;/p&gt;

&lt;p&gt;If you have used Nodemailer before, this process will be familiar. Simply create a new transport and pass it to the &lt;code&gt;email&lt;/code&gt; property in  your &lt;code&gt;payload.init()&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;Once you add your email configuration, you can send emails from anywhere in your application simply by calling &lt;code&gt;Payload.sendEmail({})&lt;/code&gt;. Neat, huh?&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuration
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;email&lt;/code&gt; property takes the following &lt;a href="https://payloadcms.com/docs/email/overview#configuration" rel="noopener noreferrer"&gt;options&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fromName - required&lt;/li&gt;
&lt;li&gt;fromAddress - required&lt;/li&gt;
&lt;li&gt;logMockCredentials - will output your credentials to the console on startup&lt;/li&gt;
&lt;li&gt;transportOptions - pass in your options and let Payload create the transport for you&lt;/li&gt;
&lt;li&gt;transport - manual create a transporter using &lt;code&gt;nodemailer.createTransport({})&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Important: You only need to use transportOptions &lt;strong&gt;OR&lt;/strong&gt; transport in your email configuration. Not both.&lt;/p&gt;

&lt;p&gt;There are two ways to create a Nodemailer-compatible transport:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Define your &lt;code&gt;transportOptions&lt;/code&gt; and Payload do it for you &lt;/li&gt;
&lt;li&gt;Manually configure a transport or import a separate package to do this for you&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To get setup, navigate to where you are calling &lt;code&gt;payload.init()&lt;/code&gt;, this is usually in `src/server.ts.&lt;/p&gt;

&lt;p&gt;After adding your email options, your &lt;code&gt;payload.init()&lt;/code&gt; should look something like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const email = {
  fromName: 'Admin',
  fromAddress: 'admin@example.com',
  logMockCredentials: true,
  transportOptions: {},   
  transport: {},
}

const start = async (): Promise&amp;lt;void&amp;gt; =&amp;gt; {
  await payload.init({
    secret: process.env.PAYLOAD_SECRET,
    mongoURL: process.env.MONGODB_URI,
    express: app,
    email,
  })

  app.listen(8000)
}

start()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Important: We always recommend storing sensitive data as environment variables and not directly in your code to prevent security vulnerabilities.&lt;/p&gt;

&lt;h4&gt;
  
  
  Mock email handler
&lt;/h4&gt;

&lt;p&gt;If you do &lt;strong&gt;not&lt;/strong&gt; provide a transport or transportOptions, Payload will initialize an &lt;a href="https://ethereal.email/" rel="noopener noreferrer"&gt;ethereal&lt;/a&gt; capture service. Ethereal is a free email caching service which captures all outbound emails. Using this service can be really useful for testing emails when you’re working in a development environment.&lt;/p&gt;

&lt;p&gt;To use this service, &lt;code&gt;logMockCredentials&lt;/code&gt; must be set to &lt;code&gt;true&lt;/code&gt;. This will output the ethereal credentials to your console after startup, you will then use these to login to &lt;a href="http://ethereal.email/" rel="noopener noreferrer"&gt;ethereal.email&lt;/a&gt; and view any emails that are sent during development.&lt;/p&gt;

&lt;h4&gt;
  
  
  transportOptions
&lt;/h4&gt;

&lt;p&gt;Pass any valid Nodemailer &lt;a href="https://nodemailer.com/smtp/" rel="noopener noreferrer"&gt;options&lt;/a&gt; to &lt;code&gt;transportOptions&lt;/code&gt; and Payload will create the transporter for you.&lt;/p&gt;

&lt;p&gt;You can use transportOptions to configure:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;SMTP&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const email = {
  fromName: 'Admin',
  fromAddress: 'admin@example.com',
  transportOptions: { 
    host: process.env.SMTP_HOST,
    auth: {
      user: process.env.SMTP_USER,
      pass: process.env.SMTP_PASS
    },
    port: 587,
    secure: false,
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;An email service &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Nodemailer will automatically provide the connection details (host, port, etc) for several &lt;a href="https://community.nodemailer.com/2-0-0-beta/setup-smtp/well-known-services/" rel="noopener noreferrer"&gt;well known email services&lt;/a&gt;. For example if you want to use Gmail, you simply need to provide the service name like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const email = {
  fromName: 'Admin',
  fromAddress: 'admin@example.com',
  transportOptions: {
    service: 'gmail',
    auth: {
      user: process.env.GMAIL_USER,
      pass: process.env.GMAIL_PASS,
    },
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;An &lt;a href="https://nodemailer.com/transports/" rel="noopener noreferrer"&gt;external transport&lt;/a&gt;, a nodemailer &lt;a href="https://www.npmjs.com/search?q=nodemailer" rel="noopener noreferrer"&gt;plugin&lt;/a&gt; or similar&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import nodemailerSendgrid from 'nodemailer-sendgrid'

export const email = {
  fromName: 'Admin',
  fromAddress: 'admin@example.com',
  transportOptions: nodemailerSendgrid({
    apiKey: process.env.SENDGRID_API_KEY,
  }),
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Nodemailer has created packages that integrate popular email vendors for you, such as &lt;a href="https://github.com/nodemailer/nodemailer-sendgrid" rel="noopener noreferrer"&gt;SendGrid&lt;/a&gt;:&lt;/p&gt;

&lt;h4&gt;
  
  
  transport
&lt;/h4&gt;

&lt;p&gt;This option allows you to manually create a transport, this supports SMTP and email services. &lt;/p&gt;

&lt;p&gt;You can make use of &lt;code&gt;nodeMailer.createTransport({})&lt;/code&gt; for support of &lt;a href="https://community.nodemailer.com/2-0-0-beta/setup-smtp/well-known-services/" rel="noopener noreferrer"&gt;well known email services&lt;/a&gt; and browse this &lt;a href="https://nodemailer.com/smtp/" rel="noopener noreferrer"&gt;list of options&lt;/a&gt; that you can define.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import nodemailer from 'nodemailer'
import payload from 'payload'

const transport = await nodemailer.createTransport({
  service: 'outlook',
  auth: {
    user: process.env.OUTLOOK_USER,
    pass: process.env.OUTLOOK_PASS,
  },
})

const email = {
  fromName: 'Admin',
  fromAddress: 'admin@example.com',
  logMockCredentials: true,
  // Passes your custom transport
  transport,
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;More examples of using nodeMailer.createTransport({}) can be found in the Nodemailer &lt;a href="https://nodemailer.com/smtp/#examples" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sending Email
&lt;/h3&gt;

&lt;p&gt;Once you have configured your transporter, you can start sending emails from anywhere inside your Payload project by calling &lt;code&gt;payload.sendEmail({})&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;payload.sendEmail({})&lt;/code&gt; takes properties of &lt;code&gt;to&lt;/code&gt;, &lt;code&gt;from&lt;/code&gt;, &lt;code&gt;subject&lt;/code&gt;, and &lt;code&gt;html&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import payload from 'payload'

payload.sendEmail({
  from: 'sender@example.com',
  to: 'receiver@example.com',
  subject: 'Message subject title',
  html: '&amp;lt;p&amp;gt;HTML based message&amp;lt;/p&amp;gt;',
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Dynamic Email Content
&lt;/h3&gt;

&lt;p&gt;There are many ways to include data directly from your project into your emails. Whether it is using hooks, making API requests, fetching data from globals or anything else you can think of.&lt;/p&gt;

&lt;p&gt;For example, sending order details when there is a new submission to the Orders collection:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import payload from 'payload'
import type { CollectionConfig } from 'payload/types'

const Orders: CollectionConfig = {
  slug: 'orders',
  hooks: {
    afterChange: [
      ({ doc, operation, req }) =&amp;gt; {
        const { customerEmail, items, total } = doc
        if (operation === 'create') {
          req.payload.sendEmail({
            to: customerEmail,
            from: 'sender@example.com',
            subject: 'Welcome To Payload',
            html: `&amp;lt;h1&amp;gt;Thank you for your order!&amp;lt;/h1&amp;gt;
              &amp;lt;p&amp;gt;Here is your order summary:&amp;lt;/p&amp;gt;
              &amp;lt;ul&amp;gt;
                ${items.map(item =&amp;gt; `&amp;lt;li&amp;gt;${item.name} - ${item.price}&amp;lt;/li&amp;gt;`)}
              &amp;lt;/ul&amp;gt;
              &amp;lt;p&amp;gt;Total: ${total}&amp;lt;/p&amp;gt;
            `,
          })
        }
      },
    ],
  },
  fields: [],
}

export default Orders
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Automatically trigger email dispatch
&lt;/h3&gt;

&lt;p&gt;Payload’s collection and field hooks allow you to define specific conditions which will trigger an email to be sent. &lt;/p&gt;

&lt;p&gt;Like sending an email every time you receive a newsletter signup:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import payload from 'payload'
import type { CollectionConfig } from 'payload/types'

const NewsletterSignups: CollectionConfig = {
  slug: 'newsletter-signups',
  hooks: {
    afterChange: [
      ({ doc, operation, req }) =&amp;gt; {
        if (operation === 'create') {
          req.payload.sendEmail({
            to: doc.email,
            from: 'sender@example.com',
            subject: 'You have joined our newsletter list!',
            html: '&amp;lt;p&amp;gt;Thanks for signing up&amp;lt;/p&amp;gt;',
          })
        }
      },
    ],
  },
  fields: [],
}

export default NewsletterSignups
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Or sending a welcome email to new users:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import payload from 'payload'
import type { CollectionConfig } from 'payload/types'

const Users: CollectionConfig = {
  slug: 'users',
  auth: true,
  hooks: {
    afterChange: [
      ({ doc, operation }) =&amp;gt; {
        if (operation === 'create') {
          payload.sendEmail({
            to: doc.email,
            from: 'sender@example.com',
            subject: 'Welcome To Payload',
            html: '&amp;lt;b&amp;gt;Hey there!&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Welcome to Payload!',
          })
        }
      },
    ],
  },
  fields: [],
}

export default Users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Authentication Emails
&lt;/h3&gt;

&lt;p&gt;Payload makes auth-enabled collections super simple to integrate with email by handling &lt;code&gt;forgotPassword&lt;/code&gt; and &lt;code&gt;verify&lt;/code&gt; for you. &lt;/p&gt;

&lt;p&gt;Each auth-enabled collection has &lt;code&gt;forgotPassword&lt;/code&gt; and &lt;code&gt;verify&lt;/code&gt; options that you can pass &lt;code&gt;generateEmailSubject&lt;/code&gt; and &lt;code&gt;generateEmailHTML&lt;/code&gt; functions to. The function accepts one argument containing &lt;code&gt;{ req, token, user }&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import payload from 'payload'
import type { CollectionConfig } from 'payload/types'

const Users: CollectionConfig = {
  slug: 'users',
  auth: {
    verify: {
      generateEmailSubject: () =&amp;gt; 'Verify your email',
      generateEmailHTML:  ({ token }) =&amp;gt; `&amp;lt;p&amp;gt;Verify your account here ${process.env.PAYLOAD_PUBLIC_SITE_URL}/verify?token=${token}.&amp;lt;/p&amp;gt;`,
    },
    forgotPassword: {
      generateEmailSubject: () =&amp;gt; 'Reset your password',
      generateEmailHTML: ({ token }) =&amp;gt; `&amp;lt;p&amp;gt;Reset your password here ${process.env.PAYLOAD_PUBLIC_SITE_URL}/reset-password?token=${token}.&amp;lt;/p&amp;gt;`,
      },
    },
  },
  fields: [],
}

export default Users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Templates
&lt;/h3&gt;

&lt;p&gt;Payload doesn't ship a default HTML templating engine, so you are free to add whatever suits you best. &lt;/p&gt;

&lt;p&gt;Make your email templates highly dynamic by using &lt;a href="https://handlebarsjs.com/guide/" rel="noopener noreferrer"&gt;Handlebars&lt;/a&gt;, a templating language that combines HTML, plain text and expressions. The expressions are included in the html template surrounded by double curly braces.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;table border="0" width="100%"&amp;gt;
  &amp;lt;tbody&amp;gt;
      &amp;lt;td&amp;gt;
        &amp;lt;!-- HEADLINE --&amp;gt;
        &amp;lt;h1&amp;gt;{{headline}}&amp;lt;/h1&amp;gt;
      &amp;lt;/td&amp;gt;
    &amp;lt;/tr&amp;gt;
    &amp;lt;tr&amp;gt;
      &amp;lt;td&amp;gt;
        &amp;lt;!-- CONTENT --&amp;gt;
        {{{content}}}
      &amp;lt;/td&amp;gt;
    &amp;lt;/tr&amp;gt;
  &amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here is a simple but powerful function that ties everything together in one function:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import fs from 'fs'
import Handlebars from 'handlebars'
import inlineCSS from 'inline-css'
import path from 'path'
import payload from 'payload'

const template = fs.readFileSync(path.join(__dirname, 'template.html'), 'utf8')
const getHTML = Handlebars.compile(template)

export const sendEmailWithTemplate = async (args): Promise&amp;lt;any&amp;gt; =&amp;gt; {
  const { from, to, subject, data } = args

  const templateData = {
    ...data,
    apiURL: process.env.PAYLOAD_PUBLIC_SERVER_URL,
    siteURL: process.env.PAYLOAD_PUBLIC_SITE_URL,
  }
  const preInlinedCSS = getHTML(templateData)

  const html = await inlineCSS(preInlinedCSS, {
    url: ' ',
    removeStyleTags: false,
  })

  await payload.sendEmail({
    from,
    to,
    subject,
    html,
  })

  return null
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;template.html&lt;/code&gt; file that is being used in &lt;code&gt;sendEmailWithTemplate&lt;/code&gt; can be any HTML file of your choice. You can find this &lt;a href="https://github.com/payloadcms/payload/tree/master/examples/email" rel="noopener noreferrer"&gt;template&lt;/a&gt; in our email example, feel free to use this as a starter template and add your own custom CSS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;We have an &lt;a href="https://github.com/payloadcms/payload/tree/master/examples/email" rel="noopener noreferrer"&gt;example repo&lt;/a&gt; where you can see these code snippets being used and try them out in real time. &lt;/p&gt;

&lt;h3&gt;
  
  
  Wrap Up
&lt;/h3&gt;

&lt;p&gt;Payload aims to provide developers with a powerful but simple solution when it comes to configuring and managing email. By harnessing the capabilities of Nodemailer, we can offer flexibility in choosing email providers and accommodate various email needs.&lt;/p&gt;

&lt;p&gt;I hope this post provides value and gives you a good introduction to email integration with Payload.&lt;/p&gt;

&lt;p&gt;Always feel free to reach out if you have any questions or feedback!&lt;/p&gt;

&lt;h3&gt;
  
  
  Learn More
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://payloadcms.com/docs/email/overview#email-functionality" rel="noopener noreferrer"&gt;Email Documentation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://nodemailer.com/about/" rel="noopener noreferrer"&gt;Nodemailer Documentation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://handlebarsjs.com/" rel="noopener noreferrer"&gt;Handlebars&lt;/a&gt; &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Like what we're doing? Give us a star on GitHub
&lt;/h3&gt;

&lt;p&gt;We're trying to change the CMS status quo by delivering editors with a great experience, but first and foremost, giving developers a CMS that they don't hate working with. All of our new features are meant to be extensible and work simply and sanely.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>typescript</category>
      <category>email</category>
      <category>payloadcms</category>
    </item>
  </channel>
</rss>
