<?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: Payload CMS</title>
    <description>The latest articles on Forem by Payload CMS (@payloadcms).</description>
    <link>https://forem.com/payloadcms</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%2Forganization%2Fprofile_image%2F3575%2F059a3321-3dff-463e-9184-2ab803bf7b5c.jpg</url>
      <title>Forem: Payload CMS</title>
      <link>https://forem.com/payloadcms</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/payloadcms"/>
    <language>en</language>
    <item>
      <title>How To Build An E-commerce Site With Next.js 💰</title>
      <dc:creator>Jacob Fletcher</dc:creator>
      <pubDate>Thu, 07 Sep 2023 20:05:12 +0000</pubDate>
      <link>https://forem.com/payloadcms/how-to-build-an-e-commerce-site-with-nextjs-383g</link>
      <guid>https://forem.com/payloadcms/how-to-build-an-e-commerce-site-with-nextjs-383g</guid>
      <description>&lt;p&gt;The last time you were tasked with building an e-commerce store, you were likely overcome with dread and fear. Your mind likely raced to one of the following pitfalls:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Time to market&lt;/li&gt;
&lt;li&gt;Microservice Hell&lt;/li&gt;
&lt;li&gt;Customer-facing UI&lt;/li&gt;
&lt;li&gt;Feature anxiety&lt;/li&gt;
&lt;li&gt;Vendor lock-in&lt;/li&gt;
&lt;li&gt;API overages&lt;/li&gt;
&lt;li&gt;Monthly fees&lt;/li&gt;
&lt;li&gt;Data ownership&lt;/li&gt;
&lt;li&gt;Database access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With &lt;a href="https://payloadcms.com" rel="noopener noreferrer"&gt;Payload's&lt;/a&gt; new &lt;a href="https://github.com/payloadcms/payload/tree/master/templates/ecommerce" rel="noopener noreferrer"&gt;E-commerce Template&lt;/a&gt; you can focus more on business strategy and less on technology with a beautifully designed, production-ready website right alongside your Payload app. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your APIs are your own and your data belongs to you.&lt;/strong&gt; You are not beholden to a third-party service that may charge you for API overages, on top of a monthly fee, and who may limit your access to your own database. &lt;/p&gt;

&lt;p&gt;Running an online store will never cost you more than your server (plus payment processing fees).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And you can go from initial clone to first sale within minutes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine actually wanting to work on your e-commerce site, being more than happy to invite marketers in to dream up new landing pages for your latest campaigns, and where editors create completely unique layouts for all types of content, all by themselves.&lt;/p&gt;

&lt;p&gt;This template is right for you if you are selling:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Physical products like clothing or merchandise&lt;/li&gt;
&lt;li&gt;Digital assets like ebooks or videos&lt;/li&gt;
&lt;li&gt;Access to content like courses or premium articles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything you need is ready to go, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Admin Dashboard&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Shopping Cart&lt;/li&gt;
&lt;li&gt;Checkout&lt;/li&gt;
&lt;li&gt;Payment Processing&lt;/li&gt;
&lt;li&gt;Customer Accounts&lt;/li&gt;
&lt;li&gt;Forms&lt;/li&gt;
&lt;li&gt;Paywall Content&lt;/li&gt;
&lt;li&gt;Layout Builder&lt;/li&gt;
&lt;li&gt;Dark Mode&lt;/li&gt;
&lt;li&gt;SEO&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And much more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option 1 (Recommended)
&lt;/h3&gt;

&lt;p&gt;Head to Payload Cloud, &lt;a href="https://payloadcms.com/new/clone/ecommerce" rel="noopener noreferrer"&gt;clone the E-commerce Template&lt;/a&gt;, and deploy it on a 30-day free trial. This process may take a few minutes, and once it's done you'll have a fully working e-commerce store, complete with a live domain. This will have created a new repo for you in your GitHub account. You can clone this repo down for local development, and when you push, your commits will automatically deploy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 2
&lt;/h3&gt;

&lt;p&gt;The code for this template is completely open source and &lt;a href="https://github.com/payloadcms/payload/tree/master/templates/ecommerce" rel="noopener noreferrer"&gt;can be found here&lt;/a&gt;. To quickly clone this template, run the following command in your terminal:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npx create-payload-app my-project -t ecommerce&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then follow the instructions in the README to get started. You &lt;a href="https://payloadcms.com/new/import" rel="noopener noreferrer"&gt;can always deploy this repo&lt;/a&gt; to Payload Cloud later.&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%2F8icdv07jduecnv7hydhd.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%2F8icdv07jduecnv7hydhd.png" alt="Sell digital assets behind a paywall with this e-commerce template" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Admin Dashboard
&lt;/h2&gt;

&lt;p&gt;As with all Payload apps, you get an enterprise-grade admin dashboard out of the box. This dashboard is where your store admins can manage all users, orders, products, pages, and more. You &lt;a href="https://payloadcms.com/blog/how-to-customize-the-look-and-feel-of-payload-with-css" rel="noopener noreferrer"&gt;can fully customize the look and feel&lt;/a&gt; and it can even be &lt;a href="https://payloadcms.com/blog/white-label-admin-ui" rel="noopener noreferrer"&gt;white-labeled to match your brand&lt;/a&gt;. The admin panel is also where your editors will use the layout builder to generate unique landing pages and product layouts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://payloadcms.com/blog/simplified-authentication-for-headless-cms-unlocking-reusability-in-one-line" rel="noopener noreferrer"&gt;Authentication with Payload was already easy&lt;/a&gt;, but it just got even easier. In this template you get fully working customer login and create account flows, complete with password reset and email verification. Forms, field states, error handling, cookies, redirects, it's all ready to go out of the box. With this template you don't need to worry about building these flows yourself, or rely on a third-party service (that you'd likely have to pay for).&lt;/p&gt;

&lt;h2&gt;
  
  
  Customer Accounts
&lt;/h2&gt;

&lt;p&gt;Users never have to leave your site to manage their account, which is a huge win for customer retention. They simply login to see their past purchases, review their order history, and update their account information. Users never have to navigate between platforms, they can do everything in one place. You can also use this data to send personalized emails to your customers, like abandoned cart reminders or product recommendations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shopping Cart
&lt;/h2&gt;

&lt;p&gt;One of the hardest parts of any e-commerce app is the shopping cart. They need to work very well in order to increase conversion and seamlessly funnel users into your checkout process. A good shopping cart should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allow users to shop without logging in&lt;/li&gt;
&lt;li&gt;Save anonymous users' carts to browser storage&lt;/li&gt;
&lt;li&gt;Sync saved carts to users' accounts upon login&lt;/li&gt;
&lt;li&gt;Persist the same cart across all devices&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Checkout
&lt;/h2&gt;

&lt;p&gt;The template integrates with &lt;a href="https://stripe.com/" rel="noopener noreferrer"&gt;Stripe&lt;/a&gt; to provide a fully working payment processing flow. &lt;/p&gt;

&lt;p&gt;In full transparency, Stripe does take a small cut of every transaction, but to be fair, they provide a valuable service by handling 100% of the financial and accounting services. It is entirely possible to integrate with other payment processors, you would just need to build out the integration yourself.&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%2Fgz2l5rwno6m49p9kqhz6.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%2Fgz2l5rwno6m49p9kqhz6.png" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Selling Digital Assets
&lt;/h2&gt;

&lt;p&gt;Gating content behind a paywall is a great way to increase your revenue. If you are selling digital assets, easily control access to those assets behind a user's purchase history. If your product is a video course or premium content, for example, you can restrict access to that course or content to only users who have purchased it. Or if you're selling an ebook, easily restrict access to a download link until the user has purchased it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layout Builder
&lt;/h2&gt;

&lt;p&gt;Your marketers will be able to dream up new landing pages and product layouts without feature anxiety. Editors will be able to create completely unique layouts for all types of content, configurable on-the-fly directly in the CMS. They can seamlessly navigate between the admin panel and the live site to see their changes in real-time. &lt;/p&gt;

&lt;p&gt;This template comes pre-configured with a few heroes and blocks to get you started, but you can easily add more.&lt;/p&gt;

&lt;h2&gt;
  
  
  SEO
&lt;/h2&gt;

&lt;p&gt;Meta tags, open-graph tags, structured data, sitemap generation, the works, it's all included. You can easily customize the SEO settings for each page and product from the admin panel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Started
&lt;/h2&gt;

&lt;p&gt;Use this template to get to market fast. Use it to enjoy building your next big e-commerce store. Or to create FOMO amongst your developer friends. &lt;a href="https://payloadcms.com/new/clone/ecommerce" rel="noopener noreferrer"&gt;Deploy in minutes on Payload Cloud&lt;/a&gt; or &lt;a href="https://github.com/payloadcms/payload/tree/master/templates/ecommerce" rel="noopener noreferrer"&gt;clone it from GitHub&lt;/a&gt;. Or, run &lt;code&gt;npx create-payload-app&lt;/code&gt;. Happy building.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Payload?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://payloadcms.com" rel="noopener noreferrer"&gt;Payload&lt;/a&gt; is a fully open-source, TypeScript-based headless CMS and application framework that gets developers what they need and gets out of the way. Built with Node.js and React, Payload is code-first, so you can enjoy a backend and a CMS-grade admin UI without having to learn another CMS. Get started in one line $npx create-payload-app or have us do the work.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ecommerce</category>
      <category>nextjs</category>
      <category>tutorial</category>
    </item>
    <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>How to Customize the Look and Feel of Payload with CSS</title>
      <dc:creator>PatrikKozak</dc:creator>
      <pubDate>Mon, 12 Jun 2023 17:56:30 +0000</pubDate>
      <link>https://forem.com/payloadcms/how-to-customize-the-look-and-feel-of-payload-with-css-1c05</link>
      <guid>https://forem.com/payloadcms/how-to-customize-the-look-and-feel-of-payload-with-css-1c05</guid>
      <description>&lt;p&gt;One of the hidden gems of Payload is its Custom CSS option.&lt;/p&gt;

&lt;p&gt;Its potential is completely limitless and lets you design / customize the appearance of every element of the Payload dashboard in a way that better represents your brand.&lt;/p&gt;

&lt;p&gt;Styling a CMS can be a pain and is usually limited. While the defaults out of the box are great, some projects deserve more finesse. Payload gives you that freedom.&lt;/p&gt;

&lt;p&gt;Our Custom CSS feature is a poster child for this approach. Wanna tweak the platform’s look? Go for it. Turn it into a kaleidoscope if you want (please don’t)... Because at the end of the day, your CMS should be a seamless extension of your brand—bonus points for making your marketing team happy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Block, Element, Modifier (BEM) Naming convention
&lt;/h3&gt;

&lt;p&gt;The entirety of the Payload admin UI utilizes the &lt;a href="https://getbem.com/" rel="noopener noreferrer"&gt;BEM (Block, Element, Modifier) naming convention&lt;/a&gt;. This widespread approach mitigates the usual challenges posed by CSS’s cascading nature, making your CSS more manageable and scalable. It also maintains a reliable selector convention, ensuring that they won’t unpredictably change on you. &lt;/p&gt;

&lt;h3&gt;
  
  
  Demo
&lt;/h3&gt;

&lt;p&gt;The first step involves providing your base Payload config with a path to your custom stylesheet, ensuring that Payload can recognize and apply your custom styles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { buildConfig } from 'payload/config';
import path from 'path';


const config = buildConfig({
    admin: {
        css: path.resolve(__dirname, 'relative/path/to/stylesheet.scss'),
    },
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you’ve set the path, you’ll need to add your stylesheet to your directory. This will serve as the space where you define your custom styles.&lt;br&gt;
First up, we’ve got a snapshot of our standard Payload Admin UI - unopinionated out of the box but highly customizable.&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%2F8chenxq5l8g7tsjc8cyt.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%2F8chenxq5l8g7tsjc8cyt.png" alt="Image description" width="800" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The image below showcases the same UI, now elegantly enhanced with some custom CSS. I’ve updated a few small things like fonts and colors to maybe match an existing brand more closely. With minimal changes, you can see the admin panel take on a different look and feel.&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%2Fxojamn5id4hdmkys9rdp.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%2Fxojamn5id4hdmkys9rdp.png" alt="Image description" width="800" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How did I do this? Starting with the browser's inspector tool, I pinpointed elements I planned to modify and inserted the necessary classes and styles into my custom stylesheet.&lt;/p&gt;

&lt;p&gt;I took a hard look at the UI’s current color theme. It was doing the job, but it wasn’t in sync with the brand image I was after. So, I changed it. By adjusting a few css variables, I’m able to change the look and feel of the dashboard to match any brand.&lt;/p&gt;

&lt;p&gt;Swapping out the font styles is easy, you simply just need to set them in your css file. Finally, I made a few adjustments to the colors of the button and the banner backgrounds, creating a more prominent contrast.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@import '~payload/scss';

html {
  &amp;amp;[data-theme=dark] {
    --theme-elevation-0: rgb(24, 20, 48);
    --theme-elevation-50: rgb(37, 33, 61);
    --theme-elevation-100: rgb(50, 46, 74);
    --theme-elevation-150: rgb(63, 59, 87);
    --theme-elevation-200: rgb(76, 72, 100);
    --theme-elevation-250: rgb(89, 85, 113);
    --theme-elevation-300: rgb(102, 98, 126);
    --theme-elevation-350: rgb(115, 111, 139);
    --theme-elevation-400: rgb(128, 124, 152);
    --theme-elevation-450: rgb(141, 137, 165);
    --theme-elevation-550: rgb(154, 150, 178);
    --theme-elevation-600: rgb(167, 163, 191);
    --theme-elevation-650: rgb(180, 176, 204);
    --theme-elevation-700: rgb(193, 189, 217);
    --theme-elevation-750: rgb(206, 202, 230);
    --theme-elevation-800: rgb(219, 215, 243);
    --theme-elevation-850: rgb(232, 228, 255);
    --theme-elevation-900: rgb(255, 255, 255);
    --theme-elevation-950: rgb(255, 255, 255);
    --theme-elevation-1000: rgb(255, 255, 255);

    --theme-bg: rgb(24, 20, 48);
  }
}

body, h2, h5 {
  font-family: monospace;
  font-weight: bold;
}

.btn {
  &amp;amp;--style-primary {
    background-color: rgb(0, 213, 110);
    color: var(--theme-elevation-900);
  }

  &amp;amp;--style-primary.btn--disabled {
    background-color: rgb(102, 255, 212);
  }

  &amp;amp;--disabled {
    background-color: rgb(102, 255, 212);
  }
}

.login .logo path {
  fill: rgb(0, 213, 110);
}

.template-default {
  .nav {
    &amp;amp;__brand .icon path {
      fill: rgb(0, 213, 110);
    }
  }

  .before-dashboard {
    .banner {
      // BEM modifier selector (.banner--type-success)
      &amp;amp;--type-success {
        background: rgb(0, 213, 110);
        color: white;
      }

      // BEM element selector (.banner__content)
      &amp;amp;__content svg {
        background-color: #2A2544;
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A quick note: see how we are nesting the targeted elements inside its parents selectors, this is what allows us to override their styles. &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity" rel="noopener noreferrer"&gt;CSS specificity&lt;/a&gt; is key here, it gives you complete control of the styles.&lt;/p&gt;

&lt;p&gt;For instance, consider our button styles. By employing a combined class selector, &lt;code&gt;.btn--style-primary.btn--disabled&lt;/code&gt;, we increased specificity to target only the disabled primary buttons—a testament to the power of CSS specificity used in tandem with BEM. &lt;/p&gt;

&lt;p&gt;If you want to target something else with more specificity, I recommend first inspecting the DOM. This helps you identify the selectors currently controlling the style. With this knowledge, you can craft a more specific selector to accurately target and style your desired elements, utilizing CSS specificity to your advantage.&lt;/p&gt;

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

&lt;p&gt;The Custom CSS feature in Payload extends beyond being a simple customization tool–it’s a pathway to enrich UX and seamlessly integrate your CMS with your brand aesthetics. &lt;/p&gt;

&lt;p&gt;We invite you to explore this feature, adapt it, and truly make it your own!&lt;/p&gt;

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

&lt;p&gt;To learn more about PayloadCMS and Custom CSS, take a look at the following resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://payloadcms.com/docs/admin/customizing-css#customizing-css-and-scss" rel="noopener noreferrer"&gt;Custom CSS &amp;amp; SCSS Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://payloadcms.com/blog/white-label-admin-ui" rel="noopener noreferrer"&gt;White Label the Admin UI Blog&lt;/a&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>css</category>
      <category>payloadcms</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>
    <item>
      <title>SQL vs. NoSQL - cutting through the Tech Twitter noise</title>
      <dc:creator>James Mikrut</dc:creator>
      <pubDate>Thu, 11 May 2023 14:42:51 +0000</pubDate>
      <link>https://forem.com/payloadcms/sql-vs-nosql-cutting-through-the-tech-twitter-noise-5ac0</link>
      <guid>https://forem.com/payloadcms/sql-vs-nosql-cutting-through-the-tech-twitter-noise-5ac0</guid>
      <description>&lt;p&gt;&lt;em&gt;We've done a lot of thinking around database support lately, and even hosted a community discussion on Discord. Right now we're MongoDB-only, but our most upvoted roadmap item is adding support for additional databases.  And transitioning from NoSQL thinking to an SQL mindset has really spotlighted the concrete differences.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you've ever wondered exactly what NoSQL is good for, and alternatively, why SQL might be a better fit for certain applications, and you're tired of Tech Twitter telling you what to think, then read on for a real-world comparison.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this post, I'm going to cover the big challenges that we'll face when adapting our features and functionalities over from NoSQL to SQL, and along the way, I'll highlight the strengths and weaknesses of each paradigm with some objective ramifications. &lt;/p&gt;

&lt;h3&gt;
  
  
  Data Definition Language
&lt;/h3&gt;

&lt;p&gt;Since we're currently solely on MongoDB, we don't need to do things manually like adding a new table column when you add a new field to your Payload config. But with SQL, we need to think about that (and make it painless).&lt;/p&gt;

&lt;p&gt;DDL, or &lt;a href="https://en.wikipedia.org/wiki/Data_definition_language" rel="noopener noreferrer"&gt;Data Definition Language&lt;/a&gt;, is how SQL databases define and maintain their structure. Even when you use something like Prisma, which takes care of a lot of the headache for you, you need to become intimately familiar with &lt;a href="https://playground.prisma.io/guides" rel="noopener noreferrer"&gt;how Prisma migrations work&lt;/a&gt;. There, Prisma translates its own schema syntax into SQL migrations for you.&lt;/p&gt;

&lt;p&gt;Don't know what any of this means? Go ahead and try to simply follow the "Create a column in a table" example. If you know SQL and if this is not new to you, then you're probably okay with this. But there's work involved here.&lt;/p&gt;

&lt;p&gt;When you decide you want to store a new field in a NoSQL database like MongoDB, you literally have to do nothing besides just start storing the data. No migrations, no generated DDL, no schema / database "sync".&lt;/p&gt;

&lt;p&gt;That's obviously a big win for NoSQL, but that also means that the NoSQL structure is significantly more loosey-goosey and that can lead to errors. Lots of devs (not to mention project requirements) straight up require the structural constraints of SQL, and allowing any type of data to be stored in a NoSQL document can present issues in terms of data integrity. So there are definitely pros and cons to both approaches.&lt;/p&gt;

&lt;h3&gt;
  
  
  Migrations
&lt;/h3&gt;

&lt;p&gt;Fascinatingly, another very hotly requested Payload feature is a first-party way of handling migrations. And that got us thinking. When you look at the Prisma migration resources above, clearly, they are using the word "migration" to describe the "migration" of your database shape from A to B. This is the expected and most widely used meaning of "migration".&lt;/p&gt;

&lt;p&gt;But we're on MongoDB, and that doesn't apply. But developers are still asking for migration support. There is a specific distinction here that I'd like to make. Migrations can mean many things in many different contexts.&lt;/p&gt;

&lt;h5&gt;
  
  
  Changing database shape
&lt;/h5&gt;

&lt;p&gt;Most often, they are referred to as simply modifying your database structure (DDL). This is a requirement for SQL in general. For example, adding a column to a table is a manual &lt;code&gt;ALTER&lt;/code&gt; that needs to be run at some point.&lt;/p&gt;

&lt;h5&gt;
  
  
  Transforming existing data
&lt;/h5&gt;

&lt;p&gt;But there's a whole different side to the story. What about if you need to change or transform your existing data? What if you had a full_name field, and you needed to split it up into two separate fields, first_name and last_name? Stuff like this is super common, and requires manual, programmatic management. Whenever anyone has asked Payload for migration support, this is what they meant.&lt;/p&gt;

&lt;p&gt;Due to this second aspect of "migrations", that means that the concept is relevant for both NoSQL and SQL databases alike. Application frameworks like Laravel handle this very well, with a full, true "migrations" workflow that allows you to handle both DDL and data-based transformations within the same paradigm, all based on database transactions. I personally think this is by far the best way to solve this and this is an often-overlooked aspect of ORMs in general. Some ORMs come with great programmatic migration support, while others solely handle the DDL side (the pain in the ass side) in raw, generated SQL. Excuse my French, but I've been spoiled here by MongoDB, and I spoiled myself intentionally.&lt;/p&gt;

&lt;p&gt;If you end up listening to our community planning call, you'll see that we cover our goals and future vision in-depth on that call. We want to model ourselves after Laravel's consistent (solve for both) approach by not outputting raw SQL, but rather, outputting TS files that give you granular, functional control.&lt;/p&gt;

&lt;h3&gt;
  
  
  Relational data and performance
&lt;/h3&gt;

&lt;p&gt;One of the biggest wins for SQL in the minds of engineers is its heavily structured relational architecture. Without a doubt, relationships are a first-class citizen in SQL. Much of the querying language itself is dedicated specifically to combining and relating data from separate tables.&lt;br&gt;
But relationships can certainly be done in NoSQL, so what's the deal? What are the real-world differences?&lt;/p&gt;

&lt;p&gt;First up, there is an entire array of functionality within SQL that actually enforces relational data integrity. Within MongoDB, those types of constraints would all need to be written manually, and you'll need to ultimately "own" the integrity of your data. In NoSQL, you might have a relationship to another document. You could go delete that document, but unless you've handled this yourself, the original relationship would remain, yet be completely invalid. That's not ideal, and that places more work on you.&lt;/p&gt;

&lt;p&gt;Let's take Payload, for example. Surprise, surprise. We have a &lt;a href="https://payloadcms.com/docs/fields/relationship" rel="noopener noreferrer"&gt;relationship field&lt;/a&gt;, and it can store IDs to other related documents which are seamlessly merged in when you retrieve documents from the DB. We leverage the &lt;a href="https://github.com/graphql/dataloader" rel="noopener noreferrer"&gt;dataloader pattern&lt;/a&gt; to batch together all "populations" required for a given query, returning them all super fast and with as few separate queries to the DB as possible.&lt;br&gt;
We actually even outperform SQL-based frameworks quite a bit. In a purely relational test, &lt;a href="https://payloadcms.com/blog/performance-benchmarks" rel="noopener noreferrer"&gt;we were 3x faster than Directus and 7x faster than Strapi&lt;/a&gt; while both were running Postgres, and we were on MongoDB.&lt;/p&gt;

&lt;p&gt;Without knowing what's under the hood of either of those platforms, my takeaway here is that relationships are possible in both NoSQL and SQL. If you were to write out queries deliberately for very specific joins between tables, and have the ability to define queries so that a single query is made vs. dynamic populations, then SQL will likely outshine MongoDB in terms of relational performance. But - your application logic will determine which is faster, and it's easy to get it wrong with either side.&lt;/p&gt;

&lt;p&gt;One thing to note here is that if you are using a GraphQL API, or any type of dynamic / expressive querying language, &lt;em&gt;it's impossible to make a single query that returns all populated "relations"&lt;/em&gt;. This is because it's up to the shape of the query to determine what to "populate". This is known as the N+1 problem. You first need to load the main document, and only then can you determine what to load after. If this applies to you, then most relational performance gains from SQL are non-applicable.&lt;/p&gt;
&lt;h3&gt;
  
  
  Handling complex data structures
&lt;/h3&gt;

&lt;p&gt;One absolutely massive win that NoSQL has over SQL is that you can store complex data directly in a single document. It can be stored in the same shape as you want it to be in when you read it back from the database. Some SQL databases like Postgres have pretty great JSON support via JSON columns, but if you over-use JSON columns in a SQL database, you're not taking advantage of the strengths of SQL in the first place and might as well go with NoSQL.&lt;/p&gt;

&lt;p&gt;So what's it look like to store a complex data structure in SQL, without losing the benefits of SQL? For a simple example, take Payload's &lt;a href="https://payloadcms.com/docs/fields/group" rel="noopener noreferrer"&gt;Group field&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"some-id-here"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"myGroup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mySubField"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"anotherSubField"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"goodbye"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above field can be stored directly in NoSQL as-is, which is fantastic. But what about SQL-based databases? Do we transform the nested architecture to a flattened column structure, using a double underscore to separate field "paths"?&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;myGroup__mySubField&lt;/th&gt;
&lt;th&gt;myGroup__anotherSubField&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;some-id-here&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hello&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;goodbye&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This could functionally perform the same, but we'd need to "transform" the data on its way out from the database somehow back to its intended shape. Doable, but not nearly as elegant.&lt;/p&gt;

&lt;p&gt;Alternatively, we could "join in" another table that includes columns that represent properties within the group. That'll become a bit less performant, because we're now doing a join that we didn't need to do in NoSQL land.&lt;/p&gt;

&lt;p&gt;Finally, do we just say "screw it" and embed the group and all of its properties, from that point on, into a single JSON column?&lt;/p&gt;

&lt;h5&gt;
  
  
  Field-based localization
&lt;/h5&gt;

&lt;p&gt;One specific feature that we support, which is simple in NoSQL, but more difficult in SQL is field-based localization. You might have 30 locales, and any given text field needs to be translated into each of the 30 locales.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"some-id-here"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"myLocalizedField"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"es"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hola"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"de"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hallo"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, in MongoDB, this is a cake-walk. Storing the locales all on a parent document is great, because they are fundamentally a part of that parent document.&lt;/p&gt;

&lt;p&gt;But in SQL we've gotta get crafty again. You might have 30 locales or more. Does that mean 30 columns for every single text field, all in one database?&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;myLocalizedField__en&lt;/th&gt;
&lt;th&gt;myLocalizedField__es&lt;/th&gt;
&lt;th&gt;myLocalizedField__de&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;some-id-here&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hello&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hola&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hallo&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you have too many columns in SQL, you'll start to see performance implications. But this needs to be solved one way or another. Again, here, we could jump to a JSON column but then we're losing a lot.&lt;/p&gt;

&lt;h5&gt;
  
  
  Array-based structures
&lt;/h5&gt;

&lt;p&gt;In NoSQL, storing arrays is dead simple. Just save it on the doc and move on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"some-id-here"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"myArray"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"subField"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hello"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"subField"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"goodbye"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The well-accepted and correct way to handle array-based data in SQL is to simply create a new table for each array-based field (like Payload's &lt;a href="https://payloadcms.com/docs/fields/array" rel="noopener noreferrer"&gt;Arrays&lt;/a&gt; and &lt;a href="https://payloadcms.com/docs/fields/" rel="noopener noreferrer"&gt;Blocks&lt;/a&gt; fields). Then you'd just join in each row of array data when you retrieve the record back. It's more complicated, sure, and we're losing some performance here because we have to make joins. But it's logical and at least the answer is clearer than the last few examples.&lt;/p&gt;

&lt;h3&gt;
  
  
  Takeaways
&lt;/h3&gt;

&lt;p&gt;Ultimately I think having a healthy dose of skepticism when reading anything that Tech Twitter or Reddit tells you will do you a great deal of good as an engineer. The truth is that there are certain cases where SQL will perform better for you, and NoSQL will be a better fit elsewhere. But it's up to your own unique requirements.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Overall, I think it's safe to say that SQL will win for your project if you:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Know the shape of your data well in advance&lt;/li&gt;
&lt;li&gt;Are confident that your schema won't change often or are comfortable dealing with lots of DDL migrations&lt;/li&gt;
&lt;li&gt;Have a relatively "flat" and not complex schema&lt;/li&gt;
&lt;li&gt;Require heavily relational structures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;On the flip-side, NoSQL will win out if you:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frequently manage complex, nested data structures like Groups, Arrays, Blocks, localized field data, and similar&lt;/li&gt;
&lt;li&gt;Use relationships solely for simple structures like Categories, Tags, Authors, etc.&lt;/li&gt;
&lt;li&gt;Want to avoid dealing with DDL stuff entirely and make your life easier&lt;/li&gt;
&lt;li&gt;Are okay with manually enforcing data integrity and handling edge cases yourself, or are using a framework that handles that stuff for you on top of NoSQL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ah, life. Everything's a compromise. As much as I'd like to go hard and declare a victor, I totally understand why Payload SQL support has been asked for, although I personally think MongoDB is still a better fit for most of our users. In the end, you'll be able to pick whatever's right for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Planning session recording
&lt;/h3&gt;

&lt;p&gt;We recorded the whole Discord planning session that took place last week. To hear more about how we're approaching future SQL support in Payload, including ORM evaluation and more, give it a listen.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/SWkVq6YxfII"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Where can you help?
&lt;/h3&gt;

&lt;p&gt;First up, we want to hear from you! If you haven't yet, make your voice and vote known on our &lt;a href="https://github.com/payloadcms/payload/discussions/287" rel="noopener noreferrer"&gt;Roadmap GitHub Discussion&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From there, once we have our future database adapter pattern established, anyone can build an adapter. We'd love to see some community-generated database and ORM support and that will only speed up / pressure test what we're building.&lt;/p&gt;

&lt;p&gt;We're really appreciative of the engagement we saw on that call and are super pumped about doing more of those in the future. The next one will likely be regarding replacing Webpack, but we'll see. &lt;a href="https://twitter.com/payloadcms" rel="noopener noreferrer"&gt;Follow us on Twitter&lt;/a&gt; to keep in the loop or &lt;a href="https://t.co/30APlsQUPB" rel="noopener noreferrer"&gt;join our Discord&lt;/a&gt; to keep an eye on the conversation there.&lt;/p&gt;

</description>
      <category>database</category>
      <category>sql</category>
      <category>nosql</category>
      <category>mongodb</category>
    </item>
    <item>
      <title>Launch Week Day 5 - Replace Shopify in Your Headless CMS Stack</title>
      <dc:creator>Sean Zubrickas</dc:creator>
      <pubDate>Fri, 07 Apr 2023 17:09:37 +0000</pubDate>
      <link>https://forem.com/payloadcms/launch-week-day-5-replace-shopify-in-your-headless-cms-stack-16m</link>
      <guid>https://forem.com/payloadcms/launch-week-day-5-replace-shopify-in-your-headless-cms-stack-16m</guid>
      <description>&lt;h2&gt;
  
  
  To wrap up our second launch week, we're releasing our Ecommerce starter kit. It demonstrates the strength of Payload and how it can be used to power much more than simple website content.
&lt;/h2&gt;

&lt;p&gt;Today, the typical custom ecommerce build workflow is to combine a headless CMS with an ecommerce vendor like Shopify or Medusa. I refuse to discuss Woocommerce so ... you're welcome. We've built projects like that in the past, and while doable, you start to have a kaleidoscope of different vendors / functions / API requests / spaghetti.&lt;/p&gt;

&lt;p&gt;One of the big takeaways from this week's launches is that we want to start to see the web get simpler, not more complex. We want to promote efficiency and reduce the time it takes for developers to build products, and from our own experience, building anything custom with ecommerce is quite the nightmare in today's ecosystem.&lt;/p&gt;

&lt;p&gt;Let's take Sanity and Shopify for example (a popular combination for custom ecommerce). Is it strange to you that both platforms can manage content, but you still need both platforms regardless, even though there's a lot of overlap?&lt;br&gt;
Let's consider an MVP ecommerce build. If you combine Shopify and Sanity, you're really only leveraging Shopify for a few aspects of your MVP:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your cart&lt;/li&gt;
&lt;li&gt;The checkout experience&lt;/li&gt;
&lt;li&gt;Processing payments&lt;/li&gt;
&lt;li&gt;Customer authentication&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you leverage these parts of Shopify, and then manage content in Sanity, here's how the workflow will look.&lt;/p&gt;

&lt;h3&gt;
  
  
  From the engineering perspective
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Fetch data from Shopify for pricing, product meta, attributes, etc.&lt;/li&gt;
&lt;li&gt;Fetch product landing page content from Sanity&lt;/li&gt;
&lt;li&gt;Combine the two into product landing pages / archives&lt;/li&gt;
&lt;li&gt;Manage separate environments for dev / stage / prod in both Sanity and Shopify, and then somehow keep them in sync&lt;/li&gt;
&lt;li&gt;Create accounts for customers in Shopify, and build the cart from Shopify&lt;/li&gt;
&lt;li&gt;In the cart, merge product meta from Shopify with whatever content is necessary from Sanity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's not too bad really. Halfway to microservices hell, but it's doable. We've done similar many times before. But now let's think about it from the marketing / business perspective.&lt;/p&gt;

&lt;h3&gt;
  
  
  From the marketing / business perspective
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;I need to add a new product. What do I do first? Build the product in Shopify, then go build the landing page in Sanity?&lt;/li&gt;
&lt;li&gt;Where do I manage product prices again? That's done in Shopify.&lt;/li&gt;
&lt;li&gt;Where do I manage the product thumbnail image? That's content, right? So Sanity. Wait, no, that's done in Shopify.&lt;/li&gt;
&lt;li&gt;I need to add a new attribute to a product. It needs a description. Content, right? OK - probably sanity. Wait. Nope, that's product meta. Gotta do that in Shopify.&lt;/li&gt;
&lt;li&gt;Rage due to confusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  This is harder than it needs to be
&lt;/h3&gt;

&lt;p&gt;When my team built projects like this in the past, frankly they didn't turn out great. We had a hard time ourselves bringing in new devs to the code because it was all a network of inter-connected mysteries. If it's remotely hard for engineers, it's certainly going to be hard for the admin end-users.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;But there's a better way. Payload offers so much more than a typical SaaS CMS, which makes this all significantly easier. Let's look at the following examples.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Payload gives you authentication&lt;/li&gt;
&lt;li&gt;You own your APIs and your database so you can write hooks to sync product meta automatically&lt;/li&gt;
&lt;li&gt;You can integrate a payments provider like Stripe seamlessly&lt;/li&gt;
&lt;li&gt;You can use Stripe webhooks thanks to the fact that you can open your own endpoints (on your own server)&lt;/li&gt;
&lt;li&gt;You can do literally ALL product management directly in one place (Payload)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Payload can eliminate Shopify from the mix entirely. Replace Shopify with Stripe, and then you have literally zero overlap between the platforms.&lt;/p&gt;

&lt;p&gt;When an admin logs in to manage products, everything is directly in Payload. When an engineer goes to build a new feature, everything is directly in Payload.&lt;/p&gt;

&lt;h3&gt;
  
  
  Announcing our Ecommerce Starter Kit
&lt;/h3&gt;

&lt;p&gt;Today, we're officially announcing our &lt;a href="https://github.com/payloadcms/template-ecommerce" rel="noopener noreferrer"&gt;Ecommerce starter kit&lt;/a&gt; which gives you everything you need to get an ecommerce store up and running instantly with Payload + NextJS. Oh, and it's completely free and open-source.&lt;/p&gt;

&lt;p&gt;No Shopify necessary. No microservices hell. You can even instantly deploy the starter to Payload Cloud, which hooks you up with everything you need to run in production.&lt;br&gt;
With this starter, you get all of the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deep integration with Stripe, including automatic two-way sync via Stripe webhooks&lt;/li&gt;
&lt;li&gt;Manage content for your entire ecommerce store in one place&lt;/li&gt;
&lt;li&gt;Next.js frontend ready to go with products, attributes, pricing, and more&lt;/li&gt;
&lt;li&gt;Customer authentication built with Payload and tied automatically to Stripe customers&lt;/li&gt;
&lt;li&gt;Shopping cart out of the box&lt;/li&gt;
&lt;li&gt;Lots, lots more&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  This is specifically cool for agencies
&lt;/h3&gt;

&lt;p&gt;Coming from an agency background, I share many of the struggles that engineers in an agency face.&lt;/p&gt;

&lt;p&gt;Each project you take on is subtly different, and you might find yourself scrambling to find the perfect tech stack for each one. You can quickly end up managing a smattering of products, each with entirely different stacks.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project A needs a simple donation form, so let's go with Contentful and a simple Stripe payment form&lt;/li&gt;
&lt;li&gt;Project B needs to sell licenses to a digital product, so let's use Sanity and Shopify for licensing and digital downloads&lt;/li&gt;
&lt;li&gt;Project C is just a brochure marketing site, so let's use Webflow&lt;/li&gt;
&lt;li&gt;Project D is an enterprise site, so let's use Contentstack&lt;/li&gt;
&lt;li&gt;Project E has subscriptions, so let's build a custom backend with Laravel and use Stripe Subscriptions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each one of these projects will be a learning experience and your engineering efficiency will suffer big time. And then fast forward a year—you have to onboard new engineers into these projects completely separately, and you'll end up with a bunch of engineers that never really understand any of the projects because they're all totally different. Revisit a project a few months later and you'll need to spend 2 days getting back up to speed with the mania that you built.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I think one of Payload's biggest shining points is that we can handle every single project in the list above. The fact that we're half application framework and half CMS is what enables all of this.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In an agency setting, imagine if you could flatten your stack. What if each project could build off the last, seamlessly? What if you could write a feature, and then the next time a client comes asking for a similar feature, you literally just re-use what you already have, but bill them as if you had to build it?\&lt;/p&gt;

&lt;p&gt;That's the dream. And that's what we're trying to give you.&lt;/p&gt;

&lt;p&gt;This ecommerce kit is the beginning of lots more to come, but it's one that we're very excited about. It'll make you build better products, save money, and be happy with your engineering workflow to top it all off.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Give the new &lt;a href="https://github.com/payloadcms/template-ecommerce" rel="noopener noreferrer"&gt;ecommerce kit&lt;/a&gt; a shot today.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ecommerce</category>
      <category>webdev</category>
      <category>programming</category>
      <category>headless</category>
    </item>
    <item>
      <title>Launch Week Day 4 - The Future of Headless</title>
      <dc:creator>Sean Zubrickas</dc:creator>
      <pubDate>Fri, 07 Apr 2023 17:09:10 +0000</pubDate>
      <link>https://forem.com/payloadcms/launch-week-day-4-the-future-of-headless-453o</link>
      <guid>https://forem.com/payloadcms/launch-week-day-4-the-future-of-headless-453o</guid>
      <description>&lt;h2&gt;
  
  
  The tech ecosystem, especially frontend, changes at light speed. It gets overwhelming to try and keep up. But I kinda love change and I think it's once again time for change in the CMS world.
&lt;/h2&gt;




&lt;blockquote&gt;
&lt;p&gt;Don't wanna read this whole monologue? Then skip right to the good stuff. Spoiler alert, you can now &lt;a href="https://github.com/payloadcms/next-payload" rel="noopener noreferrer"&gt;deploy Payload serverlessly&lt;/a&gt; on Vercel within an existing NextJS app.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;a href="https://twitter.com/steventey" rel="noopener noreferrer"&gt;Steven Tey&lt;/a&gt; and I dove into the topics covered in this blog post on a livestream yesterday.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/TWkR-OPXKRU"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Back in ~2015, I was an early advocate for headless CMS because I wanted our agency to be able to use React for the frontends of the sites we built. I had a big pitch I'd give to my clients about the value of "headless" CMS, and they bit. But now we're taking a second to re-evaluate that pitch.&lt;/p&gt;

&lt;p&gt;In my pitches, I would fire off the benefits of headless CMS, focusing on ideas like separation of concerns, more maintainable architecture, and the ability to deliver omnichannel content from a single source of truth.&lt;/p&gt;

&lt;p&gt;But in retrospect, I had to gloss over the unfortunate side-effects of headless. The simple fact is that I was pushing my technical agenda on our clients simply because I wanted to use React. Granted, we built some killer stuff for our clients and React enabled that.&lt;/p&gt;

&lt;p&gt;Lots of the other upsides to headless CMS were true, for sure. A CMS should obviously be API-first nowadays. That's a given that doesn't even need to be stated anymore. But I can count on one hand the amount of times we used a headless CMS for OmNiChAnNeL content. Most all of our headless CMS builds were just used to power a single website. Actually reading about how some of the big headless CMS position themselves as thought leaders around decentralized / omnichannel / content hub / etc. just makes me laugh at this point.&lt;/p&gt;

&lt;p&gt;Anyway, our move to headless certainly posed struggles for us along the way, especially early on. Here's a few:&lt;/p&gt;

&lt;h3&gt;
  
  
  Keeping CMS changes in sync with the frontend
&lt;/h3&gt;

&lt;p&gt;When we'd update a content model on the CMS side, we'd need to very deliberately think about how to deploy all of our changes to the CMS at the exact same time as our changes to our frontend to minimize downtime and prevent breaking code.&lt;/p&gt;

&lt;p&gt;Doesn't matter if we used Contentful, Sanity, Wordpress, whatever—the CMS being separate meant that we had to orchestrate deployments with caution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sharing TypeScript types
&lt;/h3&gt;

&lt;p&gt;A good CMS (like Payload 😈) can generate TypeScript interfaces for you which could theoretically be re-used on your frontend. But, with the separation of the CMS and the frontend, you have a few equally bad options to share these types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manually copy / paste types from the CMS to the frontend repo&lt;/li&gt;
&lt;li&gt;Publish a third-party types package that can be leveraged for both backend and frontend (still need to copy / paste)&lt;/li&gt;
&lt;li&gt;Use a monorepo and get wild with scripts to automate your copy/pasting&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Rough edges to do with "preview"
&lt;/h3&gt;

&lt;p&gt;Back in the monolithic CMS days you'd log into your CMS and then browse around on your frontend. You'd have a nice little "admin bar" that was purpose-built to give connectivity to your frontend and your backend, but we've lost a lot of that with the move to headless.&lt;/p&gt;

&lt;p&gt;There are "headless" CMSs like Storyblok who give you a full frontend visual editor, but... is that still headless? Cool in any way you look at it for sure, but I question if a CMS can call itself "headless" if it literally gives you a preview environment for your website - the "head".&lt;/p&gt;

&lt;h3&gt;
  
  
  You need to have a strong engineering team
&lt;/h3&gt;

&lt;p&gt;Lots of the pushback I would hear from my clients, and rightfully so, is something like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What happens if you and your team go away? Will we be stuck with a beautiful engineering masterpiece with no one smart enough / cost-effective enough to work on it? If we use WordPress, we can grab a developer off the street that works for cheap."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This always reminded me of Ducatis. Beautiful machines, but RIP if you have to work on one. If you've got a crazy dev environment that has three separate services all combined, sharing code in separate NPM packages, the onboarding for a developer coming into the project is gonna be steep.&lt;/p&gt;

&lt;p&gt;Let's take a second to remember that we're doing this to ourselves. All for the sake of a website in most all cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  It's time for things to get simpler
&lt;/h3&gt;

&lt;p&gt;Over the past quarter, the Payload team has been thinking about all of the above quite a bit. My thesis on the topic is that the whole web dev ecosystem perpetually swings back and forth from complexity to simplicity. Someone comes up with new tech, and it's complex, but worth it. And then people get tired of the complexity and want something simpler. And then someone makes it simpler. Repeat for the rest of our lives.&lt;/p&gt;

&lt;p&gt;I think right now we are swinging back toward simplicity. Case in point: I'm personally tired of manually solving for headless CMS "rough edges" if we're just building a simple website.&lt;/p&gt;

&lt;p&gt;Many of you already know that one of Payload's core pillars is simplicity. Is there a way that we can solve some of the above complexity issues with Payload?&lt;/p&gt;

&lt;h3&gt;
  
  
  Where we're going at Payload
&lt;/h3&gt;

&lt;p&gt;We envision a way to make Payload embeddable in just about any environment - be it an Express server, serverless functions, or edge functions. We want it to be portable so that you can install it within your website itself, no matter if your website is running Svelte, Next, Remix, Vue, Gatsby, whatever. If your environment can run Node, it should be able to run Payload.&lt;/p&gt;

&lt;p&gt;We want to promote simplicity once again. Building a website? Make it easy on yourselves, not harder. Install Payload in the same repo as your frontend. Share types, simplify dev ops, take some of the rocket science away.&lt;/p&gt;

&lt;p&gt;To realize this vision, the Payload team and I have dedicated much of the last few months to simplifying, streamlining, and modularizing our codebase so that it can be run directly in as many different types of projects as possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  The first steps
&lt;/h3&gt;

&lt;p&gt;We've simplified our code so that the Payload Local API is as barebones as possible. No HTTP overhead, no GraphQL bloat, just Node methods that are strongly typed and hit your database directly. It's almost like an ORM but it gives you an admin panel as well. This allows you to bring Payload into any project, even if it's not Express-based.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Yesterday, we announced that you can now deploy &lt;a href="https://github.com/payloadcms/next-payload" rel="noopener noreferrer"&gt;Payload serverlessly&lt;/a&gt;, within an existing NextJS app, on Vercel. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a first step, but it's a monumental shift from the "headless CMS" paradigm. You can now use Payload directly in Next server components to get strongly typed data, hitting your database directly, without any HTTP overhead. It's kinda like we're giving an optional "head" back to the headless CMS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Here's a NextJS server component
const Page = async ({ params: { slug } }) =&amp;gt; {
  const payload = await getPayload();

  // Zero HTTP overhead - this doesn't hit a REST API,
  // it goes straight to your database
  const pages = await payload.find({
    collection: "pages",
    where: {
      slug: {
        equals: slug,
      },
    },
  });

  const page = pages.docs[0];

  if (!page) return notFound();

  return (
    &amp;lt;React.Fragment&amp;gt;
      &amp;lt;AdminBar adminBarProps={{ collection: "pages", id: page.id }} /&amp;gt;
      &amp;lt;Hero {...page.hero} /&amp;gt;
      &amp;lt;Blocks blocks={page.layout} /&amp;gt;
    &amp;lt;/React.Fragment&amp;gt;
  );
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Being able to deploy Payload within your app, wherever your app is running, will solve for basically all of the above complexities that I mentioned. It'll be faster, simpler, and require less black magic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Future goals
&lt;/h3&gt;

&lt;p&gt;We're going to keep chasing this idea of simplicity. In the future, you'll be able to do this for any type of app. It doesn't need to be React or even Next. Our goal is to let you install Payload wherever you need it, and simplify your stack.&lt;/p&gt;

&lt;p&gt;We're also already working on being able to deploy Payload within edge functions. We've got a bit of work to go on that, but it'll happen.&lt;/p&gt;

&lt;h3&gt;
  
  
  Serverless not your thing? Can't deal with cold starts?
&lt;/h3&gt;

&lt;p&gt;Then you can combine Payload and NextJS into a &lt;a href="https://github.com/payloadcms/nextjs-custom-server" rel="noopener noreferrer"&gt;single server-based repo&lt;/a&gt; as well. You can even &lt;a href="https://payloadcms.com/blog/launch-week-day-1-payload-cloud-is-here" rel="noopener noreferrer"&gt;deploy it on Payload Cloud.&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  There are edge cases, of course
&lt;/h3&gt;

&lt;p&gt;I've been going on about how most people always deploy CMS in a website, blah blah. But that doesn't mean that Payload is going to lose track of our API-based DNA. There are lots of cases where deploying a CMS separately will still be the move, and any CMS should have omnichannel capabilities nowadays via REST / GraphQL / etc. no matter where or how it's deployed. I just think that is a given at this point.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;It's time the headless CMS paradigm moved back toward simplicity. With our launch of the &lt;code&gt;next-payload&lt;/code&gt; package, we're taking an important first step towards restoring simplicity.&lt;/p&gt;

&lt;p&gt;I'm not sure of what the word should be that describes what I'm trying to get to but I hate coining new words for obvious concepts.&lt;/p&gt;

&lt;p&gt;Some other CMS use the word "composable", but I feel like that's obvious if you're working with an API-based CMS. But what is true composability? Microservice / SaaS hell? Yes, let me go sign up for a SaaS CMS, then go sign up for a forms provider, then go sign up for Hubspot, then go sign up for a mail service, and then deploy my site on Next / Vercel. All just for a website? I don't care how big the site is, that doesn't sit right with me. Actually that makes me kinda want to puke.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Let's fix the complexities inherent with headless CMS and see how far we can take this idea of simplicity. Let's make our jobs easier. That'll allow us to deliver better products.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>webdev</category>
      <category>headless</category>
      <category>serverless</category>
      <category>cms</category>
    </item>
    <item>
      <title>Launch Week Day 3 - Bulk Operations</title>
      <dc:creator>Sean Zubrickas</dc:creator>
      <pubDate>Wed, 05 Apr 2023 14:23:52 +0000</pubDate>
      <link>https://forem.com/payloadcms/launch-week-day-3-bulk-operations-6lp</link>
      <guid>https://forem.com/payloadcms/launch-week-day-3-bulk-operations-6lp</guid>
      <description>&lt;h2&gt;
  
  
  The long awaited new feature lets editors edit, delete and publish many documents of a collection all at once.
&lt;/h2&gt;

&lt;p&gt;We’ve improved the editor experience by adding selection in the collection list view and new controls to Edit, Delete, and if drafts are enabled, Publish, and Unpublish. We also added API endpoints to handle the changes and also extended the local API for programmatic access. The new features are available in v1.6.24 and later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Delete, Publish, Unpublish Many
&lt;/h3&gt;

&lt;p&gt;The workflow for these are all much the same, select a bunch of documents and go for it. You’ll get a prompt to make sure you are happy with your life choices before submitting. The changes are all handled in one request. After submitting, the UI shows the number of updated documents. If there are any errors that occur due to validation or any faults in a hook on the backend, the number of documents that didn’t get updated due to error is also shown. The results are more detailed in the HTTP response or local API for your apps to deal with.&lt;/p&gt;

&lt;h3&gt;
  
  
  Edit Many
&lt;/h3&gt;

&lt;p&gt;The editing experience for multiple documents in a collection allows the editor to choose which fields to update. You can access all fields within a nested structure with the field selector and input your changes before saving. Fields that are hidden, unique or read-only are not available, and you can also configure the admin.disableBulkEdit option to remove certain fields.&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%2F0445m0xvatgmn7rf9n7m.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%2F0445m0xvatgmn7rf9n7m.png" alt="Edit multiple documents with the click of a button" width="800" height="562"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When drafts are enabled for a collection, you also have the choice to publish or save as draft. That gives you three ways to submit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Save updates the published document or most recent draft document in place without changing any statuses.&lt;/li&gt;
&lt;li&gt;Publish takes your changes, adds it to the most recent document version and publishes the results.&lt;/li&gt;
&lt;li&gt;Draft saves a draft version of your changes along with the most recent version of each document and does not affect what you currently have published.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Filter and Select All
&lt;/h3&gt;

&lt;p&gt;Instead of selecting individual rows in the list, an editor can select all documents in the list. The select-all option can be used with the existing search and filter options to only include all documents matching certain criteria. With this, the bulk edit, publish, unpublish and delete workflows will use the query to perform the operation against all the matching items.&lt;/p&gt;

&lt;h3&gt;
  
  
  API Changes
&lt;/h3&gt;

&lt;p&gt;The new functionality extends to the backend and local API as well. &lt;/p&gt;

&lt;p&gt;There are new REST endpoints for collections to allow for bulk delete and updates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PATCH /api/pages&lt;/li&gt;
&lt;li&gt;DELETE /api/pages
Each endpoint takes a query in the same form as a GET request would to make our changes against. The local APIs have also been extended to take either an id or where query.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const result = await payload.update({
      collection: 'posts',
      where: {
        id: { in: ids },
      },
      data: {
        title: 'Doc Title',
      },
    });

// result: 
// { 
//    docs: [{...}]
//    errors: []
// }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Try it out
&lt;/h2&gt;

&lt;p&gt;Update your own Payload project or head on over to the &lt;a href="https://demo.payloadcms.com" rel="noopener noreferrer"&gt;public demo&lt;/a&gt; and give it a shot.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>typescript</category>
      <category>cms</category>
    </item>
    <item>
      <title>Launch Week Day 2 - Community Help</title>
      <dc:creator>Sean Zubrickas</dc:creator>
      <pubDate>Tue, 04 Apr 2023 15:03:07 +0000</pubDate>
      <link>https://forem.com/payloadcms/launch-week-day-2-community-help-2l0g</link>
      <guid>https://forem.com/payloadcms/launch-week-day-2-community-help-2l0g</guid>
      <description>&lt;h2&gt;
  
  
  Being able to find solutions easily and leverage an active community all sharing knowledge together has a huge impact on both the quality of your code and the quality of your life.
&lt;/h2&gt;

&lt;p&gt;With this in mind, we’re excited to introduce our new &lt;a href="https://payloadcms.com/community-help" rel="noopener noreferrer"&gt;Community Help archive.&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Community Help builds upon our active and supportive communities on Discord and GitHub. With over 500 resolved threads, there's a good chance that your question has been asked before, a similar issue has been encountered or a code example that you are looking for has been shared.&lt;/p&gt;

&lt;p&gt;But the problem up until now was that GitHub Discussions are terrible for SEO for some reason, and Discord help threads are locked behind needing to download yet another app. &lt;/p&gt;

&lt;p&gt;So to make this information way more accessible, we’re directly fetching all this data from Discord/GitHub and displaying it on the Payload website’s Community Help page. (p.s. don’t forget we are open source so feel free to check out how we did this!)&lt;/p&gt;

&lt;p&gt;To help you find what you need faster, we've integrated Algolia Search to index every question, answer, and comment. This lets you use keywords to search on any part of the thread so you won’t miss out on anything relevant. You can also view the thread directly in your browser without needing to visit or sign up for external sites.&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%2Ffqw31s0xrcnqixz31sbp.jpg" 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%2Ffqw31s0xrcnqixz31sbp.jpg" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first thing most of us do when we need help is turn to a search engine (i.e…. we bing it….just kidding). But when’s the last time you ever landed on a GitHub discussion from Google? Think about it… You might land on an issue, but rarely on a discussion. &lt;/p&gt;

&lt;p&gt;With all of our GitHub Discussions and Discord community help threads output as static pages in our NextJS site, over time, we’re also hoping to start to see way more results from the Payload community directly in your Google search results.&lt;br&gt;
This section of our site will continue to evolve and become more powerful as our archive of answers expands. &lt;/p&gt;

&lt;p&gt;We’re extremely grateful for our enthusiastic and engaging community and this would not be possible without them. A few of you are absolute maniacs about helping each other and we just want to take a second to say - - THANK YOU. &lt;/p&gt;

&lt;p&gt;Head over to the &lt;a href="https://payloadcms.com/community-help" rel="noopener noreferrer"&gt;Community Help page&lt;/a&gt; and let us know what you think!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>github</category>
      <category>discord</category>
    </item>
    <item>
      <title>Launch Week Day 1 - Payload Cloud is here</title>
      <dc:creator>Sean Zubrickas</dc:creator>
      <pubDate>Tue, 04 Apr 2023 15:01:21 +0000</pubDate>
      <link>https://forem.com/payloadcms/launch-week-day-1-payload-cloud-is-here-563f</link>
      <guid>https://forem.com/payloadcms/launch-week-day-1-payload-cloud-is-here-563f</guid>
      <description>&lt;h2&gt;
  
  
  &lt;a href="https://payloadcms.com/cloud" rel="noopener noreferrer"&gt;Payload Cloud&lt;/a&gt; is now available, and completely free through beta (July 1). It's by far the most seamless way to deploy Payload for production.
&lt;/h2&gt;

&lt;p&gt;If you browse through our &lt;a href="https://t.co/30APlsQUPB" rel="noopener noreferrer"&gt;Discord community help&lt;/a&gt; forums, a big slice of the questions people ask are around deployment-related issues. &lt;/p&gt;

&lt;p&gt;We want to make it easier for those of you that don't want to deal with that racket (me included). Of course, Payload will always be open-source and able to be self-hosted, but starting today, you've got a better option.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Cloud?
&lt;/h3&gt;

&lt;p&gt;Today, we're launching the public beta of &lt;a href="https://payloadcms.com/cloud" rel="noopener noreferrer"&gt;Payload Cloud&lt;/a&gt;. It's a deployment platform that works just like Vercel, but for regular Mongo + Express apps that run a server. Connect your GitHub repo and boom — we deploy everything you need for production instantly. It's not a SaaS CMS — instead, it's &lt;strong&gt;your&lt;/strong&gt; CMS, deployed for you. We just give you the plumbing.&lt;/p&gt;

&lt;p&gt;Every piece of infrastructure that we deploy — database, S3 storage, compute, email — is exposed to you, and you can do with it what you need. We just make it easy and tailor the experience to CMS-related expectations. No more vendor hell.&lt;/p&gt;

&lt;h4&gt;
  
  
  Everything is free until July 1
&lt;/h4&gt;

&lt;p&gt;We are releasing Cloud as a beta, and that means you get everything for free until July 1. We'll foot the infrastructure bills for you until then in exchange for you helping us ensure our infrastructure is solid.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Cloud gives you
&lt;/h2&gt;

&lt;p&gt;Even on the &lt;a href="https://payloadcms.com/cloud-pricing" rel="noopener noreferrer"&gt;smallest Standard tier&lt;/a&gt;, we've done the hard stuff for you. Out of the box, you get the following:&lt;/p&gt;

&lt;h3&gt;
  
  
  Cloudflare CDN
&lt;/h3&gt;

&lt;p&gt;Your app will sit behind Cloudflare which means that you get everything that Cloudflare provides, like DDoS protection, automatic SSL certificates, a global CDN with caching, and custom domain support.&lt;/p&gt;

&lt;p&gt;We've worked tirelessly with Cloudflare to even allow you to use &lt;strong&gt;apex&lt;/strong&gt; domains, which is rare. &lt;/p&gt;

&lt;p&gt;I should take a second here to shine some light on this.&lt;/p&gt;

&lt;p&gt;Have you ever tried to deploy an app to Heroku or DigitalOcean's App Platform on your root domain? Let's say, &lt;code&gt;example.com&lt;/code&gt; - NOT a sudomain like &lt;code&gt;www.example.com&lt;/code&gt;. With most DNS providers, you can't. That's because &lt;code&gt;example.com&lt;/code&gt; is an "apex" domain, and with most DNS providers, you need to point your apex domain to a static IP like a DigitalOcean droplet. So you're forced to jump through all sorts of hoops or just make a compromise.&lt;/p&gt;

&lt;p&gt;But with Payload Cloud, we've configured our Cloudflare implementation with static IP support — so if you want to deploy your CMS and your frontend together, and host it directly on an apex domain, Payload Cloud gives you a static IP to point your domain to.&lt;/p&gt;

&lt;h3&gt;
  
  
  MongoDB Atlas
&lt;/h3&gt;

&lt;p&gt;We work closely with Mongo and have partnered with them to automatically provision a database for you, running on Atlas. Payload Cloud allows you to rely on the Atlas platform's speed, stability, and scale instantly. Your database is yours. We give you the credentials. Do with it what you will.&lt;/p&gt;

&lt;h3&gt;
  
  
  S3 file storage
&lt;/h3&gt;

&lt;p&gt;Store your files on S3 with your own bucket access. Among other things, the new &lt;code&gt;@payloadcms/plugin-cloud&lt;/code&gt; package will handle storing all of your files on S3 seamlessly for you when deployed to Payload Cloud. As with Mongo Atlas, we give you the creds, and you can do what you want with your files. They're yours.&lt;/p&gt;

&lt;h3&gt;
  
  
  App layer with automatic deploys
&lt;/h3&gt;

&lt;p&gt;No more &lt;code&gt;pm2&lt;/code&gt;. No more push / pull to a DigitalOcean droplet. Just connect your repo, and automatically deploy your code on push. Manage environment variables, monitor runtime logs, and more completely out of the box.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy your frontend, too
&lt;/h2&gt;

&lt;p&gt;I have a theory about the state and future of headless CMS. The "headless" concept came about because people wanted to bring their own frontend. Of course, the omnichannel content aspect of headless is great, but let's not kid ourselves—many of you are just powering the content for a single website / app.&lt;/p&gt;

&lt;p&gt;That's the funny thing about web development—the whole ecosystem is like a pendulum. It swings back and forth from complexity to simplicity and right now I think we're swinging back to simplicity. &lt;/p&gt;

&lt;p&gt;If the majority of us are all just powering websites with headless CMS, how about we allow ourselves to combine the frontend and backend again? The CMS can still be API-based and "headless", but if we could combine the two, we can regain some of the seamlessness that we had with monolithic solutions. It's more "composable" than "headless".&lt;/p&gt;

&lt;p&gt;That would allow us to once again do stuff like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build more efficiently and get our jobs done with less effort&lt;/li&gt;
&lt;li&gt;Deploy updates to the CMS and the frontend in tandem with each other&lt;/li&gt;
&lt;li&gt;More easily manage environments and feature sets&lt;/li&gt;
&lt;li&gt;Share TS interfaces from backend to frontend without having a third shared &lt;code&gt;utils&lt;/code&gt; NPM package or similar&lt;/li&gt;
&lt;li&gt;Build a better admin UX between frontend and backend using things like showing an "admin bar" while the user is logged in and browsing the frontend&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One of the coolest parts about Payload Cloud is that if you want, you can deploy your entire stack (CMS and frontend) directly on one service. You no longer need to deploy your CMS separately from your frontend if you don't want to. This can be huge for cases where you, as engineers, want to simplify and work as efficiently as possible.&lt;/p&gt;

&lt;p&gt;At its core, Payload Cloud is built to deploy Express + Mongo apps. Nothing more. So that means you can combine your Next + Payload apps directly in one and deploy it once. Put Remix and Payload in the same server. Run Sveltekit and Payload next to each other. Roll your own React frontend with Vite and deploy it seamlessly next to Payload.&lt;/p&gt;

&lt;p&gt;Make your job easier. There are lots of possibilities here.&lt;/p&gt;

&lt;p&gt;Through the rest of this week, we've got more announcements that make Payload able to be "composed" within your other apps, but this is definitely one of the side-effects of Payload Cloud that I'm most excited about.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's coming
&lt;/h2&gt;

&lt;p&gt;This is the very beginning for &lt;a href="https://payloadcms.com/cloud" rel="noopener noreferrer"&gt;Payload Cloud&lt;/a&gt;. Over the next few months until our exit from public Beta on July 1, we've got lots more coming.&lt;/p&gt;

&lt;h3&gt;
  
  
  Free tier
&lt;/h3&gt;

&lt;p&gt;First up, we want to launch our free tier. To do so, we need to keep working on how to reduce our own cost for this, because surprise surprise, there is no free lunch. Someone's paying for free stuff and it's gonna be us. We're happy to do so, because we want to make the developer experience for Payload as beautiful as possible. But we need to make sure that this is scalable and affordable, so we're going to try and revisit this as soon as we can.&lt;/p&gt;

&lt;h3&gt;
  
  
  Email out-of-the-box
&lt;/h3&gt;

&lt;p&gt;We're working with &lt;a href="https://resend.com/" rel="noopener noreferrer"&gt;Resend&lt;/a&gt; to give you a full, modern email service for every Cloud installation. This is almost done and will be a fast-follow to our beta launch today.&lt;/p&gt;

&lt;h3&gt;
  
  
  Programmatic CDN and purging
&lt;/h3&gt;

&lt;p&gt;We want give you a way to utilize Cloudflare's CDN for everything, programmatically, across your app + frontend. Even Payload API responses. In the future, the &lt;code&gt;@payloadcms/plugin-cloud&lt;/code&gt; package will automatically cache all applicable API responses, and will dynamically invalidate CDN cache when documents change. We'll also be exposing a way for you, as a developer, to programmatically purge CDN cache as you need.&lt;/p&gt;

&lt;h3&gt;
  
  
  On-demand backups
&lt;/h3&gt;

&lt;p&gt;Right now, MongoDB Atlas manages automated backups for you, so backups do exist out of the box already today. But we want to expose them to you via our UI, to allow you to manage and create your own backups at your discretion. We also want to extend the "backup" idea to connect to files as well, so that when you take a backup, or one is scheduled, we give you a tidy little package containing your DB and your files, ready to go for use somewhere else.&lt;/p&gt;

&lt;h3&gt;
  
  
  Environment management
&lt;/h3&gt;

&lt;p&gt;We're going to be releasing a full suite of environment management controls that will allow you to sync your DB and your files from project to project seamlessly. That will allow you to build multiple environments like &lt;code&gt;dev&lt;/code&gt;, &lt;code&gt;stage&lt;/code&gt;, and &lt;code&gt;prod&lt;/code&gt;, while being able to seamlessly migrate files + data from one environment to another.&lt;/p&gt;

&lt;h3&gt;
  
  
  Usage monitoring UI
&lt;/h3&gt;

&lt;p&gt;We have usage monitoring and reporting on our side, but we want to build this into the admin UI so that you can be informed of your usage in a simple and consistent manner directly within the Payload Cloud UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is only the beginning
&lt;/h2&gt;

&lt;p&gt;Over time, Payload Cloud will continue to evolve to be the best way to deploy Express + Mongo apps. It's only going to get better from here, and we need your help in making it so.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ready to dive in?
&lt;/h2&gt;

&lt;p&gt;Deploy your app in minutes — &lt;a href="https://payloadcms.com/new" rel="noopener noreferrer"&gt;get started&lt;/a&gt; with Payload Cloud!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>cloud</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>Redirects in Payload — Retaining SEO Value and Avoiding 404s</title>
      <dc:creator>Sean Zubrickas</dc:creator>
      <pubDate>Wed, 08 Mar 2023 13:20:42 +0000</pubDate>
      <link>https://forem.com/payloadcms/redirects-in-payload-retaining-seo-value-and-avoiding-404s-23ed</link>
      <guid>https://forem.com/payloadcms/redirects-in-payload-retaining-seo-value-and-avoiding-404s-23ed</guid>
      <description>&lt;h3&gt;
  
  
  If you're re-platforming to Payload (hooray!) you need to have a proper approach for redirects in place to ensure a successful migration.
&lt;/h3&gt;

&lt;p&gt;Having bulletproof redirect functionality in place will ensure you retain your current SEO value on existing pages and avoid any 404s. Google (mainly) and other search engines have gotten much more strict about how they rank websites, specifically those that have 404s, or pages that lead to nowhere.&lt;/p&gt;

&lt;p&gt;Additionally, the marketing team has likely done a lot of work building up their SEO ranking, and it's extremely important to keep that in place after a migration.&lt;/p&gt;

&lt;p&gt;Surprisingly, many headless content management systems don't offer redirect functionality out of the box, and you're stuck with the hassle of building this yourself.&lt;/p&gt;

&lt;p&gt;We've been there, and that's exactly why we built our redirects plugin. It's easy for you to integrate and even easier for marketers or content managers to use which lets you focus on the rest of your project.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To make it easy to familiarize yourself with our redirects plugin, we've built an example with it already installed. Clone down the &lt;a href="https://github.com/payloadcms/payload" rel="noopener noreferrer"&gt;Payload repo&lt;/a&gt; if you haven't already, and look in the examples folder for redirects. There is a straightforward readme that will get you up and running in minutes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  For the purposes of this post, I've already got our redirects example up and running so let's dive in!
&lt;/h4&gt;

&lt;p&gt;As you can see in the screenshot below, the redirects plugin creates a new collection called Redirects, which is where you'll create and manage all of your redirects.&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%2Fgg6y55wm2ekdu48p1ghu.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%2Fgg6y55wm2ekdu48p1ghu.png" alt="Image description" width="800" height="616"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, if you take a look at the example below, you'll see our payload.config is pretty straightforward. We have a pages collection, and when we go to setup our redirects we will have the ability to select documents from that collection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import redirects from '@payloadcms/plugin-redirects'
import path from 'path'
import { buildConfig } from 'payload/config'

import { Pages } from './collections/Pages'
import { Users } from './collections/Users'
import { MainMenu } from './globals/MainMenu'

export default buildConfig({
  collections: [Pages, Users],
  cors: ['http://localhost:3000', process.env.PAYLOAD_PUBLIC_SITE_URL],
  globals: [MainMenu],
  typescript: {
    outputFile: path.resolve(__dirname, 'payload-types.ts'),
  },
  plugins: [
    redirects({
      collections: ['pages'],
    }),
  ],
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you click into one of the redirects you'll see two fields.  The fields themselves are pretty straightforward - you have a From URL field, which is the URL string that will be matched up against the requested path, and the To URL field, which is conditional, and provides the option to link to a custom URL or internal doc.&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%2Fv6ocoswsb9o6550gos3w.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%2Fv6ocoswsb9o6550gos3w.png" alt="Image description" width="800" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The nice thing is that don't need to know all the ins and outs of how the plugin works, but we suspect you're going to dig around anyway and we're all for that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrap up
&lt;/h3&gt;

&lt;p&gt;We understand that redirects aren't the most exciting thing in the world, but being able to simply drop this into your project will make things much easier. With our redirects plugin, you've got a solution for almost any scenario you'll be up against.&lt;/p&gt;

&lt;p&gt;I hope you found this post helpful and that you can start utilizing this powerful plugin for your Payload project. Cheers.&lt;/p&gt;

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

&lt;p&gt;To learn more about the official redirects plugin, Payload CMS and Next.js, take a look at the following resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/payloadcms/plugin-redirects" rel="noopener noreferrer"&gt;Redirects Plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://payloadcms.com/docs" rel="noopener noreferrer"&gt;Payload CMS Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/docs" rel="noopener noreferrer"&gt;Next.js Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/learn" rel="noopener noreferrer"&gt;Learn Next.js&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Get Started
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add @payloadcms/plugin-redirects
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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 absolutely despise working with. All of our new features are meant to be extensible and work simply and sanely. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/payloadcms/payload" rel="noopener noreferrer"&gt;Stop by GitHub&lt;/a&gt; and give us a star!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>react</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Announcing the Write for the Community Program</title>
      <dc:creator>Sean Zubrickas</dc:creator>
      <pubDate>Thu, 16 Feb 2023 12:41:43 +0000</pubDate>
      <link>https://forem.com/payloadcms/announcing-the-write-for-the-community-program-2i7m</link>
      <guid>https://forem.com/payloadcms/announcing-the-write-for-the-community-program-2i7m</guid>
      <description>&lt;h2&gt;
  
  
  Payload's Write for the Community program offers a unique opportunity to reward developers for contributing to an open-source project.
&lt;/h2&gt;

&lt;p&gt;Our community has been instrumental in the breadth of features we've added to Payload over the past few years. We'd like to keep that momentum going and make it a worthwhile endeavor for all involved. That's precisely why we created the Write for the Community program.&lt;/p&gt;

&lt;p&gt;The WFC program evolved from a handful of successful collaborations with a few of our contributors. This program will encourage developers and content creators to share their knowledge and expertise with the Payload community. By contributing articles, tutorials, and other types of content, participants can help make Payload more accessible, user-friendly, and feature-rich, and get rewarded in the process.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's in it for me, exactly?
&lt;/h3&gt;

&lt;p&gt;Whether it's an article, repo, or both, we'll make it worth your while. Exact compensation will be determined by the value of the contribution. We're determined to expand our library of resources, and this program will help make that possible.&lt;/p&gt;

&lt;p&gt;In addition to the monetary rewards, you'll have the opportunity to showcase your skills and expertise, gain exposure, and build your professional network. By contributing to the Payload community, contributors can establish themselves as thought leaders and experts on Headless CMS and web development.&lt;/p&gt;

&lt;h4&gt;
  
  
  Read all about the program &lt;a href="https://payloadcms.com/write-for-the-community" rel="noopener noreferrer"&gt;here&lt;/a&gt;.
&lt;/h4&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 absolutely despise working with. All of our new features are meant to be extensible and work simply and sanely.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/payloadcms/payload" rel="noopener noreferrer"&gt;Stop by GitHub and give us a star!&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Get up and running with one line
&lt;/h3&gt;

&lt;p&gt;Getting started with Payload is easy—and free forever. Just fire up a new terminal window and run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-payload-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>news</category>
      <category>writing</category>
      <category>community</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
