<?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: James Mikrut</title>
    <description>The latest articles on Forem by James Mikrut (@jmikrut).</description>
    <link>https://forem.com/jmikrut</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F558043%2F04e1ee01-79d1-4c36-89ae-d51e5d957092.jpeg</url>
      <title>Forem: James Mikrut</title>
      <link>https://forem.com/jmikrut</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jmikrut"/>
    <language>en</language>
    <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>On GitHub Issues and Engineering Efficiency</title>
      <dc:creator>James Mikrut</dc:creator>
      <pubDate>Thu, 09 Feb 2023 19:20:39 +0000</pubDate>
      <link>https://forem.com/payloadcms/on-github-issues-and-engineering-efficiency-42fn</link>
      <guid>https://forem.com/payloadcms/on-github-issues-and-engineering-efficiency-42fn</guid>
      <description>&lt;h2&gt;
  
  
  Earlier this week, Payload hit zero open issues which is pretty wild to me. When is the last time you saw a repo with 9K stars and zero open issues?
&lt;/h2&gt;

&lt;p&gt;Obviously it's unrealistic to keep our issues at zero, but I attribute our low issue count to a lot of different tactics that we employ at Payload. First and foremost, I want to take a second to think about what the word "issue" means. Without looking it up, to me, it means "something wrong." Obviously it's got lots of meanings and can imply lots of things but go ask your grandma what it means to her. She'll probably say something like I said.&lt;/p&gt;

&lt;p&gt;Because of this, I've always had a problem with using issues for anything more than exactly that—bugs, things wrong with the software, etc. And I've been pretty adamant, for better or for worse, that Payload uses GitHub Issues solely for those types of things. I guess we engineers are suckers for semantics. Me included.&lt;/p&gt;

&lt;p&gt;Payload's love for semantics means our issues are reserved for bugs. We're not the first to do this, but it's pretty common to go to a big open-source repo and see 1 &lt;strong&gt;fafillion&lt;/strong&gt; open issues. Typically, issue labels are used to differentiate between enhancements, bugs, chores, "good first issues", etc. So if you are coming into a repo and you want to evaluate what you can expect from the product's stability / etc, you'd need to filter by label bug or similar. If we keep issues clean and true to what the word "issue" truly represents, they can paint a very accurate, crowdsourced, at-a-glance picture of what you can expect to run into with Payload. &lt;/p&gt;

&lt;p&gt;But the noise that comes from mixing bugs with features, etc. creates cognitive dissonance. It makes it difficult to see what is truly considered as a bug (officially classified by the repo maintainers) and what are issues that are maybe real, but not yet officially classified as "bug". It also muddies up the waters for the team that needs to manage those issues, because they have to filter through a sea of noise to prioritize what they should be working on. To get an accurate picture you need to figure out what labels the repo uses to classify "confirmed bugs", what labels might be used for "unconfirmed bugs", and then what issues might simply just not be labeled yet, although potentially represent real issues that you could run into. &lt;/p&gt;

&lt;p&gt;For new features, Q&amp;amp;A, help, and everything else, we point our community to our &lt;a href="https://github.com/payloadcms/payload/discussions" rel="noopener noreferrer"&gt;GitHub discussions&lt;/a&gt; or Discord &lt;a href="https://t.co/30APlsQUPB" rel="noopener noreferrer"&gt;#community-help&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub apparently doesn't like this
&lt;/h3&gt;

&lt;p&gt;Unfortunately, Payload's use of Discussions for features / roadmap items does indeed come with some drawbacks. If you're like me, you've noticed that if you Google for help with any given open-source repo, you'll end up in GitHub issues quite frequently. But when's the last time you Googled for help and ended up in GitHub Discussions? My theory is that something is amiss regarding how Google indexes GitHub content. While Issues get great SEO, Discussions barely surface themselves. I actually did a very rudimentary test of this in SF while at a GitHub event with a few GH and Payload employees and we confirmed my SEO suspicions. &lt;/p&gt;

&lt;p&gt;This was a few months ago, and we did indeed report our findings up the chain at GH, so maybe it's improved. But I would love GitHub to rectify this issue.&lt;/p&gt;

&lt;p&gt;Another thing that's sucky about using Discussions instead of Issues is that you can't use Discussions within GitHub Projects. I would love to use GitHub Projects for our roadmap. I'm a sucker for a Gantt chart. But we can't surface Discussions in Roadmaps at all. Big Bummer.&lt;/p&gt;

&lt;p&gt;So we've made do with our Roadmap existing entirely in Discussions, holding out hope for GitHub to improve this at some point in the future. At that point, I'll drink a Bud Heavy nodding to GitHub for a job well done, and for sticking to my semantic guns.&lt;/p&gt;

&lt;h3&gt;
  
  
  Issues for Payload are top priority
&lt;/h3&gt;

&lt;p&gt;If you've been around for a while, you'll notice that we're fastest at responding to issues. We do our best to respond to Discord threads, GitHub discussions, and issues all the same - but issues get our attention immediately. When an issue opens, the whole team has a tendency to just drop what we're doing and take a look at what's happening. &lt;/p&gt;

&lt;p&gt;It's really nice for our engineers if our issues are as moderated as possible. By establishing the pattern that Payload issues are reserved to "bugs", this greatly reduces the amount of times that our engineers get distracted by help discussions and feature requests. Of course we still do our best to reply to all community help, especially including discussions, but generally discussions are totally OK with a response that comes within a day or so vs. an issue being addressed more quickly.&lt;/p&gt;

&lt;p&gt;Even still, keeping issues as our top priority does indeed cause distractions. And that creates friction, breaking our engineering trains-of-thought quite frequently. &lt;/p&gt;

&lt;p&gt;You could be knee deep in a new feature at Payload but then see our #dev-feed Slack channel light up with a new issue report. It's tempting to just shut down work on your new feature and go check out what's going on. I'm 100% guilty of this myself and sometimes feel like I'm on a hamster wheel, just running around day after day trying to close issues as diligently as I can.&lt;/p&gt;

&lt;p&gt;But the problem goes deeper. It exacerbates itself if you consider that one engineer might need support from other engineers closer to the feature in order to properly solve it. Might need a rubber duck. Might just want to pair with someone to work through the best solution. Then all of a sudden you'll have two or more engineers distracted—their trains of thought interrupted by the issue butterfly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Our attempt to keep focus - Bugfix Mondays
&lt;/h3&gt;

&lt;p&gt;To alleviate these ever-so-alluring distractions, we have established a sort of "triage" approach to new issue reports. If it's significant, it gets addressed as soon as possible. But for all other issues, we'll respond and acknowledge—but then we will try and save the fix until Mondays.&lt;/p&gt;

&lt;p&gt;On Mondays, every engineer at Payload spends the whole day working through issues together. In the morning, we all review each open issue together, assigning issues to those that are best suited to solve the issue. And then we go our separate ways - setting up reproduction environments, and picking through the Payload logic to determine what's necessary to fix the issue.&lt;/p&gt;

&lt;p&gt;So far, this is has been hugely successful for a variety of reasons. The first is that we get to avoid distractions, which allows our engineering team to focus for the other 4 days of the work week. &lt;/p&gt;

&lt;p&gt;But second, and possibly much more important, is that if everyone is in "fix it" mode at the same time, we can bounce back and forth to help each other, brainstorm solutions, and review fixes. &lt;/p&gt;

&lt;p&gt;So if engineer A was assigned a bug, but engineer B built the original feature, engineer A can ping B to say "hey, I'd like your eyes on this when you have a sec" and then once B is done, they can jump over to help A. &lt;/p&gt;

&lt;p&gt;Knowledge is shared, effectiveness is ensured, and distractions are basically expected. But that's part of what makes our Mondays fun.&lt;/p&gt;

&lt;p&gt;Of course this won't work for teams that get over a certain size, but right now, we're scrappy and it's fun. It's also pretty effective.&lt;/p&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;br&gt;
&lt;a href="https://github.com/payloadcms/payload" rel="noopener noreferrer"&gt;&lt;strong&gt;Stop by GitHub and give us a star!&lt;/strong&gt;&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>github</category>
      <category>webdev</category>
      <category>opensource</category>
      <category>typescript</category>
    </item>
    <item>
      <title>It's 2022 - time to stop using headless WordPress and ACF</title>
      <dc:creator>James Mikrut</dc:creator>
      <pubDate>Tue, 04 Oct 2022 13:17:43 +0000</pubDate>
      <link>https://forem.com/payloadcms/its-2022-time-to-stop-using-headless-wordpress-and-acf-o9j</link>
      <guid>https://forem.com/payloadcms/its-2022-time-to-stop-using-headless-wordpress-and-acf-o9j</guid>
      <description>&lt;h3&gt;
  
  
  In 2016, WordPress + ACF was one of the best ways to build a headless CMS. ACF made it useful. But it was never ideal.
&lt;/h3&gt;

&lt;p&gt;The Payload team has built so many projects with headless WordPress + ACF, and at one time, it was the best tool for the job. ACF features like Conditional Logic and the "Flexible Content" field made it worth the WP headache. &lt;/p&gt;

&lt;p&gt;But it always felt so wrong in so many ways. When we started building Payload, part of our goal was to take what ACF did well—but do it even better. We were set on not making any compromises. If ACF had a core feature that we used, Payload should have it as well. The same experience created in WP should be better in every single way if built in Payload. &lt;/p&gt;

&lt;p&gt;That means in regards to developer experience, editor experience, speed, maintainability—everything.&lt;/p&gt;

&lt;h3&gt;
  
  
  Payload is a strong WordPress alternative
&lt;/h3&gt;

&lt;p&gt;Now that Payload 1.0 is released, there's no longer a reason for developers to build on headless WP. We built a repo that shows how you'd typically build something in WP, and how you can replicate the same thing with Payload.&lt;/p&gt;

&lt;p&gt;Check out the video below to see what makes headless WP a bad choice for modern devs, and why Payload is so much better suited to the job.&lt;/p&gt;

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

&lt;h4&gt;
  
  
  Give us a star on GitHub
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;If you haven't already, stop by our GitHub page and &lt;a href="https://github.com/payloadcms/payload" rel="noopener noreferrer"&gt;leave us a star&lt;/a&gt; by clicking on the star icon in the top right corner. This helps us grow and gain exposure within the development community.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Join our community on Discord
&lt;/h4&gt;

&lt;p&gt;We've recently started a Discord community for the Payload community to interact in realtime. Often, Discord will be the first to hear about announcements like this move to open-source, and it can prove to be a great resource if you need help building with Payload. &lt;a href="https://discord.com/invite/r6sCXqVk3v" rel="noopener noreferrer"&gt;Click here&lt;/a&gt; to join!&lt;/p&gt;

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

&lt;p&gt;Getting started 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>wordpress</category>
      <category>javascript</category>
      <category>react</category>
      <category>cms</category>
    </item>
    <item>
      <title>Speed test - Payload vs. Directus vs. Strapi</title>
      <dc:creator>James Mikrut</dc:creator>
      <pubDate>Wed, 28 Sep 2022 13:42:59 +0000</pubDate>
      <link>https://forem.com/payloadcms/speed-test-payload-vs-directus-vs-strapi-4jn3</link>
      <guid>https://forem.com/payloadcms/speed-test-payload-vs-directus-vs-strapi-4jn3</guid>
      <description>&lt;h3&gt;
  
  
  Payload is a powerful alternative to Strapi or Directus, and in this post, we stacked them up in a head-to-head GraphQL performance test.
&lt;/h3&gt;

&lt;p&gt;Disclaimer: clearly we're a bit partial to Payload, but we've done our best to make sure this test is as fair as possible. We are only comparing features that Payload, Directus, and Strapi equally support and are attempting to run the most fair comparison possible. That said, the results are very interesting and the test repos themselves serve as a little bonus showing how vastly different the real-world project workflows are from Payload to Directus to Strapi.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;All of the test code is &lt;a href="https://github.com/payloadcms/speed-test" rel="noopener noreferrer"&gt;available and open-source&lt;/a&gt;. You can take a look at how we seed, what the queries we use look like, and how the test itself is structured. You can also run the tests yourself!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Links to seed scripts:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/payloadcms/speed-test/blob/main/payload/src/seed.ts" rel="noopener noreferrer"&gt;Payload&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/payloadcms/speed-test/blob/main/directus/scripts/seed.ts" rel="noopener noreferrer"&gt;Directus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/payloadcms/speed-test/blob/main/strapi/scripts/seed.ts" rel="noopener noreferrer"&gt;Strapi&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Links to GraphQL queries used:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/payloadcms/speed-test/blob/main/payload/query.graphql" rel="noopener noreferrer"&gt;Payload&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/payloadcms/speed-test/blob/main/directus/query.graphql" rel="noopener noreferrer"&gt;Directus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/payloadcms/speed-test/blob/main/strapi/query.graphql" rel="noopener noreferrer"&gt;Strapi&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And finally, here's a &lt;a href="https://github.com/payloadcms/speed-test/blob/main/test.ts" rel="noopener noreferrer"&gt;link to the performance testing script&lt;/a&gt; itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  The idea behind the test
&lt;/h3&gt;

&lt;p&gt;With this performance test, we wanted to see how a real-world, complex document query might fare while retrieved from the three different CMS' GraphQL endpoints. Let's consider a complex "mega menu" document, where there may be 30-50 "links" to other pages / posts / etc. and lots of media relations like icons, images, etc. that need to be rendered in a given mega menu. Just with that one mega menu document, we might have to retrieve a ton of "related" documents, with lots of JSON coming back from the response. &lt;/p&gt;

&lt;p&gt;In our past experience, this can quickly become problematic (especially if you are server-side rendering) because that mega menu "document" is used by and needs to be retrieved for &lt;strong&gt;every single server-rendered view&lt;/strong&gt; of a given app or website. That means that unless your CMS is heavily optimized, you're going to need to shell out some cash to make sure your server can handle this type of request. To make matters worse, modern frontend frameworks like Gatsby or Next often pre-render views, which means that during the build process, your server could get hammered with requests to your API.&lt;/p&gt;

&lt;h4&gt;
  
  
  Document structure we're testing against
&lt;/h4&gt;

&lt;p&gt;To reflect a moderately complex real-world query, we designed a document structure that features 60+ relationships as well as complex data structures like groups, arrays, nested arrays, and blocks. The document itself is seeded predictably and exactly in the same manner through all three content management systems, and the GraphQL queries that are run are exactly the same outside of specific CMS syntax differences.&lt;/p&gt;

&lt;h4&gt;
  
  
  Ensuring environment parity
&lt;/h4&gt;

&lt;p&gt;In all CMS benchmarks, we worked with a local dev environment and used local databases so that latency was eliminated as a factor. The database contents for each CMS benchmark were closely controlled so as to ensure that the number of documents / rows within each CMS database was as similar to one another as possible. The machine we used to test for all three vendors was a 16" Macbook Pro 2021, M1 Max with 32GB of RAM. Node version was &lt;code&gt;16.13.1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We then wrote a simple script that could be shared by all three CMS benchmarks to hammer out 100 fetch requests sequentially, each with the same query, to the GraphQL HTTP endpoint. We then report on total test time, min response time, max response time, and average response time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Seeding data to test with
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;In Payload&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because everything in Payload is code-based, seeding is super easy. We find that for local development, seeding is an absolute must - because that way you don't need to manually click around and create documents each time to test with, and the codebase can be spun up quickly by as many team members as necessary.&lt;/p&gt;

&lt;p&gt;For this reason, in our own projects, we typically set the &lt;code&gt;env&lt;/code&gt; variable &lt;code&gt;PAYLOAD_DROP_DATABASE=true&lt;/code&gt; so that the database is dropped upon every server start, and then we seed an initial set of documents to test and build with. This is a super awesome dev pattern and really increases our team's velocity. It's also reusable for test suites and can help big time with automated testing. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Bonus:&lt;/strong&gt; Payload ships with a &lt;a href="https://payloadcms.com/docs/local-api/overview" rel="noopener noreferrer"&gt;Local API&lt;/a&gt; that can be used to efficiently seed documents with. It runs code directly on your Node server, with no need to go through the REST or GraphQL HTTP layers. This means it's blazingly fast (even faster than our GraphQL performance) and it's perfect for local seeding. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;In Directus&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With Directus, seeding is quite a bit more challenging because Directus is not code-based. Rather, your field configs are stored in your database itself, so there were a lot of steps for us to get this up and running. &lt;/p&gt;

&lt;p&gt;We first had to initialize a project and create a first user to authenticate with. Then we had to design the field schema via pointing and clicking in the Directus UI (not in code). We then wrote a seed script to use the Directus SDK in order to generate some documents via the REST API. The seed script itself was pretty tricky to write because the data that we needed to pass to "relationship" fields is pretty Directus-specific and we had to do some reverse-engineering to figure it out. &lt;/p&gt;

&lt;p&gt;Finally, we had to create an SQL dump of the database, which at this point contains all our fields and our first user. We stored the SQL dump in our repo for developers to be able to easily replicate this test without having to manually create a new project and configure all fields. But once you import the database dump, you still need to manually run the seed script to populate the database with documents to test against.&lt;/p&gt;

&lt;p&gt;This is all quite a bit more complex than what you have to do in Payload. With Payload, a developer coming into the project for the first time just runs &lt;code&gt;yarn dev&lt;/code&gt;. That's it. With Directus, there are quite a few more steps. We spent about a day trying to figure out if there was any way to export collection configs / re-import them into a new project but we gave up because we couldn't find anything in the docs. There are &lt;a href="https://github.com/directus/directus/issues/2927" rel="noopener noreferrer"&gt;some discussions about adding import / export endpoints&lt;/a&gt; which would be a great feature, but as of now the process was a bit difficult for us.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In Strapi&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It took us quite a while to figure out some oddities of the way everything works in Strapi. For example, the concept of an "admin user" is completely different than a "regular user", and we needed to write a shell script to create the admin user from the Strapi CLI. We got stuck for a while trying to authenticate via REST with our "admin user", only to find out that those users are different from regular "users". That's certainly a "gotcha".&lt;/p&gt;

&lt;p&gt;For seeding, we opted not to use a &lt;code&gt;config/functions/bootstrap.js&lt;/code&gt;, which seems to be the recommended solution. We found this to be an incomplete solution as it didn't allow us to create users or modify permissions. We ended up using a combination of scripts to create an admin user as well as authenticated user, load permissions using the &lt;a href="https://market.strapi.io/plugins/strapi-plugin-config-sync" rel="noopener noreferrer"&gt;Config Sync&lt;/a&gt; plugin's import command, then seeding the complex documents using the REST API. Again, with Strapi, there are quite a few hoops to jump through.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benchmarks
&lt;/h2&gt;

&lt;p&gt;Now that we had documents of the same data shape seeded within each CMS, we set off to run the benchmark tests themselves.&lt;/p&gt;

&lt;h4&gt;
  
  
  #1. Payload
&lt;/h4&gt;

&lt;p&gt;We knew that Payload was fast and we place a lot of emphasis on ensuring that it performs as quickly as possible, but with our recent addition of the &lt;a href="https://github.com/graphql/dataloader" rel="noopener noreferrer"&gt;dataloader pattern&lt;/a&gt;, our result surprised even us.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Average response time&lt;/td&gt;
&lt;td&gt;15ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Min response time&lt;/td&gt;
&lt;td&gt;8ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max response time&lt;/td&gt;
&lt;td&gt;43ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total test duration&lt;/td&gt;
&lt;td&gt;1,513ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  #2. Directus
&lt;/h4&gt;

&lt;p&gt;Directus came in second with some interesting results. The max response time was quite high, but average was not bad. Quite a bit slower than Payload, but still reasonable.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Average response time&lt;/td&gt;
&lt;td&gt;45ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Min response time&lt;/td&gt;
&lt;td&gt;24ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max response time&lt;/td&gt;
&lt;td&gt;139ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total test duration&lt;/td&gt;
&lt;td&gt;4,459ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  #3. Strapi
&lt;/h4&gt;

&lt;p&gt;Strapi fell quite a bit behind both Payload and Directus and this is interesting to us because of the SQL-based nature of Strapi 4, and the relational nature of our tests.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Average response time&lt;/td&gt;
&lt;td&gt;102ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Min response time&lt;/td&gt;
&lt;td&gt;77ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max response time&lt;/td&gt;
&lt;td&gt;353ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total test duration&lt;/td&gt;
&lt;td&gt;10,172ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;blockquote&gt;
&lt;p&gt;On average, Payload is &lt;strong&gt;3x faster than Directus&lt;/strong&gt; and &lt;strong&gt;almost 7x faster than Strapi&lt;/strong&gt; in response time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We're super proud of the efficiency that we've been able to produce with Payload and this is only the beginning. Our team is expanding, and we have lots of plans in store over the next few months including even more UI optimizations, new features, and lots of tutorials / example boilerplates. Oh, and Payload Cloud. Keep an eye out for that one because it's going to be awesome.   &lt;/p&gt;

&lt;h4&gt;
  
  
  Give Payload a star
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;We're completely free, MIT-licensed and open-source. If you haven't already, stop by our GitHub page and &lt;a href="https://github.com/payloadcms/payload" rel="noopener noreferrer"&gt;&lt;strong&gt;leave us a star&lt;/strong&gt;&lt;/a&gt; by clicking on the star icon in the top right corner. This helps us grow and gain exposure within the development community.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Get started in one line
&lt;/h4&gt;

&lt;p&gt;If you haven't yet given Payload a shot, you can get started with one 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>javascript</category>
      <category>webdev</category>
      <category>typescript</category>
      <category>programming</category>
    </item>
    <item>
      <title>Announcing the official Payload Cloud Storage Plugin</title>
      <dc:creator>James Mikrut</dc:creator>
      <pubDate>Tue, 09 Aug 2022 15:15:00 +0000</pubDate>
      <link>https://forem.com/payloadcms/announcing-the-official-payload-cloud-storage-plugin-381o</link>
      <guid>https://forem.com/payloadcms/announcing-the-official-payload-cloud-storage-plugin-381o</guid>
      <description>&lt;h3&gt;
  
  
  Payload just released its official Cloud Storage plugin, with support for file storage in AWS S3 and Azure Blob Storage out of the box.
&lt;/h3&gt;

&lt;p&gt;Payload plugins can be insanely powerful, and they're trivial to write thanks to Payload's code-first, config-based nature. We've already seen an impressive array of community plugins, including a few great cloud storage solutions from &lt;a href="https://github.com/richardvanbergen/payload-plugin-cloud-storage" rel="noopener noreferrer"&gt;Richard VanBergen&lt;/a&gt; and &lt;a href="https://github.com/alexbechmann/payload-plugin-azure-blob-storage" rel="noopener noreferrer"&gt;Alex Bechmann&lt;/a&gt;. We're extremely thankful for our community, especially Richard and Alex in regards to their help with cloud storage.&lt;/p&gt;

&lt;p&gt;Over the past few months, enterprise clients of ours have asked quite a few times for us to build an officially supported cloud storage plugin, so that all files uploaded to Payload can be stored in the cloud storage provider of their choice. Of course, by default, Payload allows you to store uploads on &lt;strong&gt;your own server&lt;/strong&gt;, but at scale, you might want to offload them to a CDN-backed service for performance and scalability reasons.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So we built a robust, officially supported cloud storage plugin.&lt;/strong&gt; It's extremely powerful and comes out-of-the-box with Amazon S3 and Azure Blob Storage support. The plugin is built with an adapter-based approach, so you can use it with any cloud storage provider that you wish.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The plugin is available now - check it out on &lt;a href="https://github.com/payloadcms/plugin-cloud-storage" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; or &lt;a href="https://www.npmjs.com/package/@payloadcms/plugin-cloud-storage" rel="noopener noreferrer"&gt;NPM&lt;/a&gt;!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;Installing and using the plugin is easy. Here's what it looks like to use the S3 adapter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;buildConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;payload/config&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Media&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./collections/Media&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cloudStorage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@payloadcms/plugin-cloud-storage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;s3Adapter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@payloadcms/plugin-cloud-storage/s3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&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;collections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Media&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="c1"&gt;// Pass the plugin to Payload&lt;/span&gt;
    &lt;span class="nf"&gt;cloudStorage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Enable cloud storage for Media collection&lt;/span&gt;
        &lt;span class="na"&gt;media&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// Create the S3 adapter&lt;/span&gt;
          &lt;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;s3Adapter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3_ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;accessKeyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;secretAccessKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3_SECRET_ACCESS_KEY&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="na"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3_BUCKET&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;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;p&gt;With this plugin, instead of saving files locally to your server, Payload will ship off all uploads, including all automatically resized images, straight to your cloud storage provider.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adapter-based Implementation
&lt;/h3&gt;

&lt;p&gt;The plugin is built on a simple, yet powerful adapter-based implementation. This means it can be used for any type of cloud storage you need. Out of the box, it comes with Azure Blob Storage and AWS S3 support, but you can easily write your own adapter(s) to host your uploads wherever you need.&lt;/p&gt;

&lt;h3&gt;
  
  
  Access Control
&lt;/h3&gt;

&lt;p&gt;Payload comes with built-in access control for all uploads. It works seamlessly by re-using your upload-enabled collection's &lt;code&gt;read&lt;/code&gt; access control—meaning you can easily restrict who can access which uploads that are stored in your site. This is super powerful in lots of cases, and is a specific strength of Payload over other headless CMS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Imagine if you had a public Customers collection, and your customers could upload sensitive documents to attach to their profile.&lt;/strong&gt; You may only want certain users to be able to view these sensitive documents - i.e. admins or the customers themselves. But most people should be denied access.&lt;/p&gt;

&lt;p&gt;With Payload, this is built-in. And this plugin fully supports this functionality, so you can build powerful and elegant access control patterns to protect your files—even when they're hosted on your cloud provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get started
&lt;/h2&gt;

&lt;p&gt;Get started by installing the plugin in your Payload app with 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;yarn add @payloadcms/plugin-cloud-storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From there, &lt;a href="https://github.com/payloadcms/plugin-cloud-storage" rel="noopener noreferrer"&gt;check out the docs&lt;/a&gt; to learn about all the things you can do.&lt;/p&gt;

&lt;h3&gt;
  
  
  Give Payload a star
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;If you haven't already, stop by our GitHub page and &lt;strong&gt;&lt;a href="https://github.com/payloadcms/payload" rel="noopener noreferrer"&gt;leave us a star&lt;/a&gt;&lt;/strong&gt; by clicking on the star icon in the top right corner. This helps us grow and gain exposure within the development community.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Request a Plugin
&lt;/h4&gt;

&lt;p&gt;Need a plugin to be created? &lt;a href="https://discord.com/invite/r6sCXqVk3v" rel="noopener noreferrer"&gt;Join our Discord server&lt;/a&gt; and post in the &lt;code&gt;#plugins&lt;/code&gt; channel. The Payload team might create it for you, or maybe a community member can help!&lt;/p&gt;

&lt;h4&gt;
  
  
  Get started in one line
&lt;/h4&gt;

&lt;p&gt;If you haven't yet given the CMS a shot, you can get started free with one 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>javascript</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>react</category>
    </item>
    <item>
      <title>Tutorial - Configuring TypeScript, Express, Jest, Payload, and VSCode Debugging from Scratch</title>
      <dc:creator>James Mikrut</dc:creator>
      <pubDate>Thu, 02 Jun 2022 12:48:37 +0000</pubDate>
      <link>https://forem.com/payloadcms/tutorial-configuring-typescript-express-jest-payload-and-vscode-debugging-from-scratch-84d</link>
      <guid>https://forem.com/payloadcms/tutorial-configuring-typescript-express-jest-payload-and-vscode-debugging-from-scratch-84d</guid>
      <description>&lt;h4&gt;
  
  
  Setting up a TypeScript project from scratch can be intimidating. Bring in Jest, VSCode debugging, and a headless CMS - and there are many moving parts. But it doesn't have to be difficult!
&lt;/h4&gt;

&lt;p&gt;We've been working hard at making Payload + TypeScript a match made in heaven, and over the last few months we've released a suite of features, including &lt;a href="https://payloadcms.com/docs/typescript/generating-types" rel="noopener noreferrer"&gt;automatic type generation&lt;/a&gt;, which makes Payload by far the best TypeScript headless CMS available. To celebrate, we're going to show you how to scaffold a TypeScript and Express project from scratch, including testing it with Jest and debugging with VSCode.&lt;/p&gt;

&lt;p&gt;By understanding just a few new concepts, you can master your dev environment's setup to maximize productivity and gain a deep understanding how it all works together. Let's get started.&lt;/p&gt;

&lt;h4&gt;
  
  
  Software Requirements
&lt;/h4&gt;

&lt;p&gt;Before going further, make sure you have the following software:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Yarn or NPM&lt;/li&gt;
&lt;li&gt;NodeJS&lt;/li&gt;
&lt;li&gt;A Mongo Database&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Step 1 - initialize a new project
&lt;/h4&gt;

&lt;p&gt;Create a new folder, &lt;code&gt;cd&lt;/code&gt; into it, and initialize:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir ts-payload &amp;amp;&amp;amp; cd ts-payload &amp;amp;&amp;amp; yarn init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 2 - install dependencies
&lt;/h4&gt;

&lt;p&gt;We'll need a few baseline dependencies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dotenv&lt;/code&gt; - to set up our environment easily&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;express&lt;/code&gt; - Payload is built on top of Express&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ts-node&lt;/code&gt; - to execute our TypeScript project in development mode&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;typescript&lt;/code&gt; - base TS dependency&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;payload&lt;/code&gt; - no description necessary&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nodemon&lt;/code&gt; - to make sure our project restarts automatically when files change&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install these dependencies by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add dotenv express payload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the rest as devDependencies using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add --dev nodemon ts-node typescript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 3 - create a tsconfig:
&lt;/h4&gt;

&lt;p&gt;In the root of your project folder, create a new file called &lt;code&gt;tsconfig.json&lt;/code&gt; and add the following content to it. This file tells the TS compiler how to behave, including where to write its output files.&lt;/p&gt;

&lt;p&gt;Example &lt;code&gt;tsconfig.json&lt;/code&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;"compilerOptions"&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;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"es5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lib"&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="s2"&gt;"dom"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"dom.iterable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"esnext"&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;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"skipLibCheck"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"esModuleInterop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"commonjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"moduleResolution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"allowSyntheticDefaultImports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"resolveJsonModule"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"isolatedModules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"jsx"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"preserve"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sourceMap"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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;"include"&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="s2"&gt;"src"&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;"ts-node"&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;"transpileOnly"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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;In the above example config, we're planning to keep all of our TypeScript files in &lt;code&gt;/src&lt;/code&gt;, and then build to &lt;code&gt;/dist&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 4 - set up your .env file
&lt;/h4&gt;

&lt;p&gt;We'll be using &lt;code&gt;dotenv&lt;/code&gt; to manage our environment and get ourselves set up for deployment to various different environments like staging and production later down the road. The &lt;code&gt;dotenv&lt;/code&gt; package will  read all values in a &lt;code&gt;.env&lt;/code&gt; file within our project root and bind their values to &lt;code&gt;process.env&lt;/code&gt; so that you can access them in your code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you're using GitHub, make sure you ignore your environment file from your  repository. Often, environment files store sensitive information and should not be included in source code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's create a &lt;code&gt;.env&lt;/code&gt; file in the root folder of your project and add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PORT=3000
MONGO_URL=mongodb://localhost/your-project-name
PAYLOAD_SECRET_KEY=alwifhjoq284jgo5w34jgo43f3
PAYLOAD_CONFIG_PATH=src/payload.config.ts
PAYLOAD_PUBLIC_SERVER_URL=http://localhost:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure that the &lt;code&gt;MONGO_URL&lt;/code&gt; line in your &lt;code&gt;.env&lt;/code&gt; matches an available MongoDB instance. If you have Mongo running locally on your computer, the line above should work right out of the box, but if you want to use a hosted MongoDB like Mongo Atlas, make sure you copy and paste the connection string from your database and update your &lt;code&gt;.env&lt;/code&gt; accordingly.&lt;/p&gt;

&lt;p&gt;For more information on what these values do, take a look at &lt;a href="https://payloadcms.com/docs/getting-started/installation#server" rel="noopener noreferrer"&gt;Payload's Getting Started docs&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 5 - create your server
&lt;/h4&gt;

&lt;p&gt;Setting up an Express server might be pretty familiar. Create a &lt;code&gt;src/server.ts&lt;/code&gt; file in your project and add the following to the file:&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;payload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Use `dotenv` to import your `.env` file automatically&lt;/span&gt;
&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../.env&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PAYLOAD_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;mongoURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MONGO_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;express&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Express is now listening for incoming connections on port &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;p&gt;The file above first imports our server dependencies. Then, we use &lt;code&gt;dotenv&lt;/code&gt; to load our &lt;code&gt;.env&lt;/code&gt; file at our project root. Next, we initialize Payload by providing it with our secret key, Mongo connection string, and Express app. Finally, we tell our Express app to listen on the port defined in our &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 6 - create a nodemon file
&lt;/h4&gt;

&lt;p&gt;We'll use Nodemon to automatically restart our server when any &lt;code&gt;.ts&lt;/code&gt; files change within our &lt;code&gt;./src&lt;/code&gt; directory. Nodemon will execute &lt;code&gt;ts-node&lt;/code&gt; for us, which will use our server as its entry point. Create a &lt;code&gt;nodemon.json&lt;/code&gt; file within the root of your project and add the following content.&lt;/p&gt;

&lt;p&gt;Example &lt;code&gt;nodemon.json&lt;/code&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;"watch"&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="s2"&gt;"./src/**/*.ts"&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;"exec"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ts-node ./src/server.ts"&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;h4&gt;
  
  
  Step 7 - add your Payload config
&lt;/h4&gt;

&lt;p&gt;The Payload config is central to everything Payload does. Add it to your &lt;code&gt;src&lt;/code&gt; folder and enter the following baseline code:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;./src/payload.config.ts&lt;/code&gt;:&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;dotenv&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;buildConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;payload/config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../.env&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&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;serverURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PAYLOAD_PUBLIC_SERVER_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;typescript&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;outputFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./generated-types.ts&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;collections&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="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;useAsTitle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title&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;fields&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;author&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;relationship&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;relationTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&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;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;p&gt;This config is very basic - but check out &lt;a href="https://payloadcms.com/docs/configuration/overview" rel="noopener noreferrer"&gt;the Config docs&lt;/a&gt; for more on what the Payload config can do. Out of the box, this config will give you a default &lt;code&gt;Users&lt;/code&gt; collection, a simple &lt;code&gt;Posts&lt;/code&gt; collection with a few fields, and will open up the admin panel to you at &lt;code&gt;http://localhost:3000/admin&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The config also specifies where Payload should output its auto-generated TypeScript types which is super cool (we'll come back to this).&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 8 - add some NPM scripts
&lt;/h4&gt;

&lt;p&gt;The last step before we can fire up our project is to add some development, build, and production NPM scripts.&lt;/p&gt;

&lt;p&gt;Open your &lt;code&gt;package.json&lt;/code&gt; and update the &lt;code&gt;scripts&lt;/code&gt; property to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "scripts": {
    "generate:types": "PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:types",
    "dev": "nodemon",
    "build:payload": "PAYLOAD_CONFIG_PATH=src/payload.config.ts payload build",
    "build:server": "tsc",
    "build": "yarn build:server &amp;amp;&amp;amp; yarn build:payload",
    "serve": "PAYLOAD_CONFIG_PATH=dist/payload.config.js NODE_ENV=production node dist/server.js"
  },
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To support Windows environments consider adding the &lt;code&gt;cross-env&lt;/code&gt; package as a devDependency and use it in scripts before setting variables.&lt;/p&gt;

&lt;p&gt;The first script uses Payload's &lt;code&gt;generate:types&lt;/code&gt; command in order to automatically generate TypeScript types for each of your collections and globals automatically. You can run this command whenever you need to regenerate your types, and then you can use these types in your Payload code directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Payload's ability to automatically generate types from your configs is super powerful and will benefit you immensely if you are writing custom hooks or custom components.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The next script is to execute &lt;code&gt;nodemon&lt;/code&gt;, which will read the &lt;code&gt;nodemon.json&lt;/code&gt; config that we've written and execute our &lt;code&gt;/src/server.ts&lt;/code&gt; script, which fires up Payload in development mode.&lt;/p&gt;

&lt;p&gt;The following three scripts are how we will prepare Payload's admin panel for production as well as how to compile the server's TypeScript code into regular JS to use in production.&lt;/p&gt;

&lt;p&gt;Finally, we have a &lt;code&gt;serve&lt;/code&gt; script which is used to serve our app in production mode once it's built.&lt;/p&gt;

&lt;h3&gt;
  
  
  Firing it up
&lt;/h3&gt;

&lt;p&gt;We're ready to go! Run &lt;code&gt;yarn dev&lt;/code&gt; in the root of your folder to start up Payload. Then, visit &lt;code&gt;http://localhost:3000/admin&lt;/code&gt; to create your first user and sign into the admin panel.&lt;/p&gt;

&lt;h4&gt;
  
  
  Generating Payload types
&lt;/h4&gt;

&lt;p&gt;Now that we've got a server generated, let's try and generate some types. Run the following command to automatically generate a file that contains an interface for the default &lt;code&gt;Users&lt;/code&gt; collection and our simple &lt;code&gt;Posts&lt;/code&gt; collection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn generate:types
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then check out the file that was created at &lt;code&gt;/src/generated-types.ts&lt;/code&gt;. You can import these types in your own code to do some pretty awesome stuff.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing with Jest
&lt;/h2&gt;

&lt;p&gt;Now it's time to get some tests written. There are a ton of different approaches to testing, but we're going to go straight for end-to-end tests. We'll use Jest to write our tests, and set up a fully functional Express server before we start our tests so that we can test against something that's as close to production as possible.&lt;/p&gt;

&lt;p&gt;We'll also use &lt;code&gt;mongodb-memory-server&lt;/code&gt; to connect to for all tests, so that we don't have to clutter up our development database with testing documents. This is great, because our tests will be totally controlled and isolated, but coverage will be incredibly thorough due to how we'll be testing the full API from top to bottom.&lt;/p&gt;

&lt;p&gt;Payload will automatically attempt to use &lt;code&gt;mongodb-memory-server&lt;/code&gt; if two conditions are met:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It is locally installed in your project&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NODE_ENV&lt;/code&gt; is equal to &lt;code&gt;test&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Adding and configuring test dependencies
&lt;/h4&gt;

&lt;p&gt;OK. Let's install all the testing dependencies we'll need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add --dev jest mongodb-memory-server babel-jest @babel/core @babel/preset-env @babel/preset-typescript isomorphic-fetch @types/jest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's add two new config files. First, &lt;code&gt;babel.config.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;presets&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@babel/preset-env&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;targets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;current&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@babel/preset-typescript&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@babel/preset-react&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use Babel so we can write tests in TypeScript, and test full React components.&lt;/p&gt;

&lt;p&gt;Next, &lt;code&gt;jest.config.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;verbose&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;testEnvironment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;globalSetup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;rootDir&amp;gt;/src/tests/globalSetup.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;roots&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="s1"&gt;&amp;lt;rootDir&amp;gt;/src/&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Global Test Setup
&lt;/h4&gt;

&lt;p&gt;A sharp eye might find that we're using a &lt;code&gt;globalSetup&lt;/code&gt; file in &lt;code&gt;jest.config.js&lt;/code&gt; to scaffold our project before any of the real magic starts. Let's add that file:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/tests/globalSetup.ts&lt;/code&gt;:&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="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;testCredentials&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./credentials&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../.env&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PAYLOAD_PUBLIC_SERVER_URL&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;globalSetup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PAYLOAD_PUBLIC_SERVER_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/users/first-register`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;testCredentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;testCredentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;headers&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="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&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;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;post&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to register first user&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;globalSetup&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this file, we're performing the following actions before our tests are executed:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Importing the server, which will boot up Express and Payload using &lt;code&gt;mongodb-memory-server&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Loading our &lt;code&gt;.env&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Registering a first user so that we can authenticate in our tests&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You'll notice we are importing &lt;code&gt;testCredentials&lt;/code&gt; from next to our &lt;code&gt;globalSetup&lt;/code&gt; file. Because our Payload API will require authentication for many of our tests, and we're creating that user in our &lt;code&gt;globalSetup&lt;/code&gt; file, we will want to reuse our user credentials in other tests to ensure we can authenticate as the newly created user. Let's create a reusable file to store our user's credentials:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/tests/credentials.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test@test.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Our first test
&lt;/h4&gt;

&lt;p&gt;Now that we've got our global setup in place, we can write our first test.&lt;/p&gt;

&lt;p&gt;Add a file called &lt;code&gt;src/tests/login.spec.ts&lt;/code&gt;:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../generated-types&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;testCredentials&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./credentials&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;isomorphic-fetch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Users&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should allow a user to log in&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PAYLOAD_PUBLIC_SERVER_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/users/login`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&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="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&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;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;testCredentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;testCredentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeDefined&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;p&gt;The test above is written in TypeScript and imports our auto-generated &lt;code&gt;User&lt;/code&gt; TypeScript interface to properly type the &lt;code&gt;fetch&lt;/code&gt; response that is returned from Payload's &lt;code&gt;login&lt;/code&gt; REST API.&lt;/p&gt;

&lt;p&gt;It will expect that a token is returned from the response.&lt;/p&gt;

&lt;h4&gt;
  
  
  Running tests
&lt;/h4&gt;

&lt;p&gt;The last step is to add a script to execute our tests. Let's add a new line to our &lt;code&gt;package.json&lt;/code&gt; &lt;code&gt;scripts&lt;/code&gt; property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    "test": "jest --forceExit --detectOpenHandles"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can run &lt;code&gt;yarn test&lt;/code&gt; to see a successful test!&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging with VSCode
&lt;/h2&gt;

&lt;p&gt;Debugging can be an absolutely invaluable tool to developers working on anything more complex than a simple app. It can be difficult to understand how to set up, but once you have it configured properly, a proper debugging workflow can be significantly more powerful than just relying on &lt;code&gt;console.log&lt;/code&gt; all the time.&lt;/p&gt;

&lt;p&gt;You can debug your application itself, and you can even debug your tests to troubleshoot any tests that might fail in the future. Let's see how to set up VSCode to debug our new Typescript app and its tests.&lt;/p&gt;

&lt;p&gt;First, create a new folder within your project root called &lt;code&gt;.vscode&lt;/code&gt;. Then, add a &lt;code&gt;launch.json&lt;/code&gt; within that folder, containing the following configuration:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;./.vscode/launch.json&lt;/code&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;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"configurations"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"launch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Launch Program"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&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;"PAYLOAD_CONFIG_PATH"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/payload.config.ts"&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;"program"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${workspaceFolder}/src/server.ts"&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Debug Jest Tests"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"launch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"runtimeArgs"&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="s2"&gt;"--inspect-brk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"${workspaceRoot}/node_modules/.bin/jest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"--runInBand"&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;"env"&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;"PAYLOAD_CONFIG_PATH"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/payload.config.ts"&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;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 file above includes two configurations. The first is to be able to debug your Express + Payload app itself, including any files you've imported within your Payload config. The second debug config is to be able to set breakpoints and debug directly within your Jest testing suite.&lt;/p&gt;

&lt;p&gt;To debug, you can set breakpoints right in your code by clicking to the left of the line numbers. A breakpoint will be set and show as a red circle. When VSCode executes your scripts, it will pause at the breakpoint(s) you set and allow you to inspect the value of all variables, where your function(s) have been called from, and much more.&lt;/p&gt;

&lt;p&gt;To start the debugger, click the "Run and Debug" sidebar icon in VSCode, choose the debugger you want to start, and click the "Play" button. If you've placed breakpoints, VSCode will automatically pause when it reaches your breakpoint.&lt;/p&gt;

&lt;p&gt;Here is an example of a breakpoint being hit within our &lt;code&gt;src/server.ts&lt;/code&gt; file:&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%2Fpayloadcms.com%2Fimages%2Fblog%2Fts-tutorial%2Fdebug-express.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%2Fpayloadcms.com%2Fimages%2Fblog%2Fts-tutorial%2Fdebug-express.jpg" alt="Debugging Express and Payload using TypeScript in VSCode" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is a screenshot of a breakpoint being hit within our &lt;code&gt;login.spec.ts&lt;/code&gt; test:&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%2Fpayloadcms.com%2Fimages%2Fblog%2Fts-tutorial%2Fdebug-jest.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%2Fpayloadcms.com%2Fimages%2Fblog%2Fts-tutorial%2Fdebug-jest.jpg" alt="Debugging a TypeScript Jest app in VSCode" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Debugging can be an invaluable tool to you as a developer.&lt;/strong&gt; Setting it up early in your project will pay dividends over time as your project gets more complex, and it will help you understand how your project works to an extremely fine degree.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With all of the above pieces in place, you have a modern and well-equipped dev environment that you can use to build out Payload into anything you can think of - be it an API to power a &lt;a href="https://payloadcms.com/use-cases/web-apps" rel="noopener noreferrer"&gt;web app&lt;/a&gt;, &lt;a href="https://payloadcms.com/use-cases/native-apps" rel="noopener noreferrer"&gt;native app&lt;/a&gt;, or just a headless CMS to &lt;a href="https://payloadcms.com/use-cases/websites" rel="noopener noreferrer"&gt;power a website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/payloadcms/typescript-jest-vscode" rel="noopener noreferrer"&gt;You can find the code for this guide here&lt;/a&gt;. Let us know what you think!&lt;/p&gt;

&lt;h3&gt;
  
  
  Star us on GitHub
&lt;/h3&gt;

&lt;p&gt;If you haven't already, stop by GitHub and &lt;a href="https://github.com/payloadcms/payload" rel="noopener noreferrer"&gt;give us a star&lt;/a&gt; by clicking on the "Star" button in the top-right corner of our repo. With our &lt;a href="https://payloadcms.com/blog/announcing-yc" rel="noopener noreferrer"&gt;inclusion into YC&lt;/a&gt; and our &lt;a href="https://payloadcms.com/blog/open-source" rel="noopener noreferrer"&gt;move to open-source&lt;/a&gt;, we're looking to dramatically expand our community and we can't do it without you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Join our community on Discord
&lt;/h3&gt;

&lt;p&gt;We've recently started a Discord community for the Payload community to interact in realtime. Often, Discord will be the first to hear about announcements like this move to open-source, and it can prove to be a great resource if you need help building with Payload. &lt;a href="https://discord.com/invite/r6sCXqVk3v" rel="noopener noreferrer"&gt;Click here&lt;/a&gt; to join!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Building a Professionally Designed Website Episode 4 - Heroes, Layout Building Blocks, Animations &amp; Design</title>
      <dc:creator>James Mikrut</dc:creator>
      <pubDate>Tue, 24 Aug 2021 12:03:09 +0000</pubDate>
      <link>https://forem.com/payloadcms/building-a-professionally-designed-website-episode-4-heroes-layout-building-blocks-animations-design-1f5e</link>
      <guid>https://forem.com/payloadcms/building-a-professionally-designed-website-episode-4-heroes-layout-building-blocks-animations-design-1f5e</guid>
      <description>&lt;h3&gt;
  
  
  This is Part 4 in our deep-dive into how to develop a professionally designed, high-end NextJS + Payload CMS website from start to finish.
&lt;/h3&gt;

&lt;p&gt;In this episode, we'll finish out the entire site. Buckle up—it's a long one. You'll see how to develop a super dynamic layout builder that puts the exact right amount of control into your admins' hands. You'll see how a wide variety of layout blocks are built while all relying on a very solid React + TypeScript codebase. In addition to the layout building components, we'll go through each Hero style that we've designed, show how the Form integration works, and add some miscellaneous polish to the site here and there.&lt;/p&gt;

&lt;p&gt;By the end of this video, the site will be fully complete.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Table of Contents
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=0s" rel="noopener noreferrer"&gt;0:00​&lt;/a&gt;​ - Intro&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=151s" rel="noopener noreferrer"&gt;2:31&lt;/a&gt; - Payload Roadmap&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=251s" rel="noopener noreferrer"&gt;4:11&lt;/a&gt; - Dynamically generated admin descriptions&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=307s" rel="noopener noreferrer"&gt;5:07&lt;/a&gt;​ - Plugins infrastructure&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=401s" rel="noopener noreferrer"&gt;6:41&lt;/a&gt; - Remaining work to be done&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=600s" rel="noopener noreferrer"&gt;10:00&lt;/a&gt; - Picking up where we left off&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=677" rel="noopener noreferrer"&gt;11:17&lt;/a&gt; - Building the Heroes&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=755" rel="noopener noreferrer"&gt;12:35&lt;/a&gt; - Extending Payload's Rich Text Editor&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=989" rel="noopener noreferrer"&gt;16:29&lt;/a&gt; - Field Conditional Logic&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=1325" rel="noopener noreferrer"&gt;22:05&lt;/a&gt; - Converting SlateJS Rich Text into JSX&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=1535" rel="noopener noreferrer"&gt;25:35&lt;/a&gt; - Creating custom Rich Text Editor components on the frontend&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=2232" rel="noopener noreferrer"&gt;37:12&lt;/a&gt; - Media component&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=2254" rel="noopener noreferrer"&gt;37:34&lt;/a&gt; - Payload's "depth" function&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=2621" rel="noopener noreferrer"&gt;43:41&lt;/a&gt; - Beginning the layout blocks&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=2842" rel="noopener noreferrer"&gt;47:22&lt;/a&gt; - The Call to Action block&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=3553" rel="noopener noreferrer"&gt;59:13&lt;/a&gt; - The Content block&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=3612" rel="noopener noreferrer"&gt;1:00:12&lt;/a&gt; - More Rich Text Editor customization&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=4514" rel="noopener noreferrer"&gt;1:15:14&lt;/a&gt; - The Media block&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=4583" rel="noopener noreferrer"&gt;1:16:23&lt;/a&gt; - Parallax component&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=4958" rel="noopener noreferrer"&gt;1:22:38&lt;/a&gt; - Slider block&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=5460" rel="noopener noreferrer"&gt;1:31:00&lt;/a&gt; - Study Slider block&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=5659" rel="noopener noreferrer"&gt;1:34:19&lt;/a&gt; - Spacer block&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=5719" rel="noopener noreferrer"&gt;1:35:19&lt;/a&gt; - Statistics block&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=6305" rel="noopener noreferrer"&gt;1:45:05&lt;/a&gt; - Sticky Content block&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=6963" rel="noopener noreferrer"&gt;1:56:03&lt;/a&gt; - Forms &amp;amp; Form Submissions&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=8102" rel="noopener noreferrer"&gt;2:15:02&lt;/a&gt; - Background Noise&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=hIc0WYqdDto&amp;amp;t=8250" rel="noopener noreferrer"&gt;2:17:30&lt;/a&gt; - Outro&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The design of the site was done by &lt;a href="https://trbl.design" rel="noopener noreferrer"&gt;TRBL&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Code for this episode
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://github.com/payloadcms/custom-website-series/tree/episode-4" rel="noopener noreferrer"&gt;https://github.com/payloadcms/custom-website-series/tree/episode-4&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Figma link
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://www.figma.com/file/Ul801GH5yjcwEA3JVJH90F/Website-Series?node-id=713%3A1940" rel="noopener noreferrer"&gt;https://www.figma.com/file/Ul801GH5yjcwEA3JVJH90F/Website-Series?node-id=713%3A1940&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  The real-world, live website
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://metricstructures.com" rel="noopener noreferrer"&gt;https://metricstructures.com&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Payload for free
&lt;/h3&gt;

&lt;p&gt;Check out Payload for free, forever. The &lt;a href="https://www.youtube.com/pricing" rel="noopener noreferrer"&gt;Personal License&lt;/a&gt; gives you everything Payload does, all for free. We think you'll find Payload's developer experience second to none. It just feels &lt;em&gt;right&lt;/em&gt; to build sites and apps with it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feedback welcome
&lt;/h3&gt;

&lt;p&gt;As always, let us know what you think about the video - and about Payload!&lt;/p&gt;

</description>
      <category>react</category>
      <category>css</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The Payload Headless CMS just got more powerful with Plugins</title>
      <dc:creator>James Mikrut</dc:creator>
      <pubDate>Wed, 28 Jul 2021 13:58:51 +0000</pubDate>
      <link>https://forem.com/payloadcms/the-payload-headless-cms-just-got-more-powerful-with-plugins-4dm2</link>
      <guid>https://forem.com/payloadcms/the-payload-headless-cms-just-got-more-powerful-with-plugins-4dm2</guid>
      <description>&lt;h2&gt;
  
  
  Payload's new and robust Plugins infrastructure makes reusing and modularizing your Payload code super easy.
&lt;/h2&gt;

&lt;p&gt;Because Payload is built as a code-first, config-based headless CMS, using third-party plugins and even writing your own is extremely straightforward. All that's necessary is a little knowledge of JavaScript, and then from there, you can extend and modify Payload in any way that you can dream up.&lt;/p&gt;

&lt;p&gt;With Plugins, Payload can be extended and modified in a ton of ways. Over time, we're going to build up a library of commonly requested and officially supported plugins—but what's really exciting is the fact that anyone can make their own with just a little bit of JS knowledge.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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="c1"&gt;// .. your config here&lt;/span&gt;
    &lt;span class="na"&gt;serverURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="c1"&gt;// plugins are just simple JS functions&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;passwordProtect&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="nf"&gt;syncToHubSpot&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customers&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Easy to learn, easy to write
&lt;/h3&gt;

&lt;p&gt;Most CMS plugin systems are upsettingly difficult to learn and require you to devote yourself to learning how to build a plugin for &lt;em&gt;that&lt;/em&gt; CMS. You might be able to cobble something together after spending a day or so of time learning how to work in someone else's system, but you'll never get that time back. Worse yet - your learning will only ever apply to that one platform.&lt;/p&gt;

&lt;p&gt;Payload is different. Because the core infrastructure of Payload is so open-ended, being based fully on vanilla JS / TS, the knowledge that you need to build Payload plugins is universally applicable in your career as a developer. Writing a Payload plugin is as easy as taking in a config, and then returning an enhanced config. Within, you can rely on Payload's already powerful infrastructure design like Hooks, function-based access control, custom components, open-ended Express usage, and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Possibilities
&lt;/h2&gt;

&lt;p&gt;The possibilities run a lot wider than what you might expect at first glance. You can add and enhance just about any part of Payload. Below, we'll write out some examples.&lt;/p&gt;

&lt;h4&gt;
  
  
  CRM Connector (MailChimp, HubSpot, Salesforce, etc.)
&lt;/h4&gt;

&lt;p&gt;Imagine if your &lt;code&gt;Customers&lt;/code&gt; collection could be automatically synced with HubSpot, MailChimp, or similar each time a customer is created or updated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Required plugin functions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add an &lt;code&gt;afterChange&lt;/code&gt; hook to relevant collections that takes the incoming data and sends it to your CRM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Easy. The plugin would just take in a user's config, copy it, inject some hooks containing your platform's integration logic, and return the new config.&lt;/p&gt;

&lt;h3&gt;
  
  
  Field encryption
&lt;/h3&gt;

&lt;p&gt;If you're working with sensitive data, you might want to encrypt certain fields, so that even when their data is stored at rest in the database, it is not in plaintext. Common examples might be passwords, API keys, HIPAA data, credit card data, or other personally identifiable information. A Plugin could be easily written to automatically encrypt/decrypt data as it enters and exits your APIs. The plugin would need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a &lt;code&gt;beforeChange&lt;/code&gt; hook to all fields needing to be encrypted that converts the incoming value to an encrypted counterpart&lt;/li&gt;
&lt;li&gt;Add an &lt;code&gt;afterRead&lt;/code&gt; hook too all encrypted fields which decrypts them automatically as they are sent out by your APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Form builder
&lt;/h3&gt;

&lt;p&gt;A common use case for sites and apps is to expose a fully featured Form Builder to your editors. One way to do this through a Plugin would be as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inject a new collection called &lt;strong&gt;Forms&lt;/strong&gt;. The Forms collection would come with a &lt;code&gt;blocks&lt;/code&gt; field type, where each &lt;code&gt;block&lt;/code&gt; would represent a field type (&lt;code&gt;select&lt;/code&gt;, &lt;code&gt;text&lt;/code&gt;, &lt;code&gt;checkbox&lt;/code&gt;, etc.) Editors could then build out their own custom forms and build &lt;code&gt;relationship&lt;/code&gt; fields which would reference them.&lt;/li&gt;
&lt;li&gt;Inject a new collection called &lt;strong&gt;Form Submissions&lt;/strong&gt;. This new collection would accept JSON submissions and automatically validate their incoming data compared to the corresponding Form.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Password-protection
&lt;/h3&gt;

&lt;p&gt;What if you needed password protection over documents in certain collections? A plugin could do this easily. It would need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatically inject some new fields into password-enabled collections&lt;/li&gt;
&lt;li&gt;Add a new REST endpoint as well as a new GraphQL mutation for end-users to provide a password, and have their password be validated&lt;/li&gt;
&lt;li&gt;Add a &lt;code&gt;beforeRead&lt;/code&gt; hook to all appropriate collections that shows and hides the document based on if the user has access&lt;/li&gt;
&lt;li&gt;Conditionally show and hide existing fields in the admin panel based on if the user has access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Stay tuned for this one.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the first officially-supported plugin that Payload will release. You'll be able to read the source code to see how it's done.&lt;/p&gt;

&lt;h5&gt;
  
  
  Other possibilities
&lt;/h5&gt;

&lt;p&gt;Here are some other quick example use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a full ecommerce backend to any Payload app&lt;/li&gt;
&lt;li&gt;Add custom reporting views to Payload's admin panel&lt;/li&gt;
&lt;li&gt;Integrate all &lt;code&gt;upload&lt;/code&gt;-enabled collections with a third-party file host like S3 or Cloudinary&lt;/li&gt;
&lt;li&gt;Add custom routes or GraphQL queries / mutations with any type of custom functionality that you can think of&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Documentation
&lt;/h2&gt;

&lt;p&gt;Find more information, including a simple example plugin, in our &lt;a href="https://payloadcms.com/docs/plugins/overview" rel="noopener noreferrer"&gt;Plugin documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Request a Plugin
&lt;/h4&gt;

&lt;p&gt;Need a plugin to be created? Start a &lt;a href="https://github.com/payloadcms/payload/discussions" rel="noopener noreferrer"&gt;GitHub discussion&lt;/a&gt;. If we don't build it, maybe someone else has - or will!&lt;/p&gt;

&lt;h4&gt;
  
  
  Let us know your thoughts
&lt;/h4&gt;

&lt;p&gt;We've got big things planned for Payload, and we'd love to hear what you think. If you haven't yet given the CMS a shot, you can get started free with one 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>javascript</category>
      <category>node</category>
      <category>react</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Building a Professionally Designed Website Episode 3 - Header &amp; Footer, Hamburger Menu, Animating SVGs</title>
      <dc:creator>James Mikrut</dc:creator>
      <pubDate>Mon, 07 Jun 2021 19:51:12 +0000</pubDate>
      <link>https://forem.com/payloadcms/building-a-professionally-designed-website-episode-3-header-footer-hamburger-menu-animating-svgs-17ln</link>
      <guid>https://forem.com/payloadcms/building-a-professionally-designed-website-episode-3-header-footer-hamburger-menu-animating-svgs-17ln</guid>
      <description>&lt;h3&gt;
  
  
  This is Part 3 in our deep-dive into how to develop a professionally designed, high-end NextJS + Payload CMS website from start to finish.
&lt;/h3&gt;

&lt;p&gt;In this episode, we're diving deep into React. You'll learn how a design agency organizes its components and you'll start to see a lot of the CSS boilerplate that we defined in Episode 2 start being put to use. We're going to build and animate the Header, Footer, and Mega Menu—all in React and TypeScript.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Table of Contents
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=vSEcBiyNe8Y&amp;amp;t=0s" rel="noopener noreferrer"&gt;0:00&lt;/a&gt;​ - Intro&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=vSEcBiyNe8Y&amp;amp;t=213s" rel="noopener noreferrer"&gt;3:33&lt;/a&gt; - The logo - an SVG React component&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=vSEcBiyNe8Y&amp;amp;t=722s" rel="noopener noreferrer"&gt;12:02&lt;/a&gt;- Animating the hamburger button&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=vSEcBiyNe8Y&amp;amp;t=1513s" rel="noopener noreferrer"&gt;25:13&lt;/a&gt;​ - Retrieving menu global data from Payload&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=vSEcBiyNe8Y&amp;amp;t=1947s" rel="noopener noreferrer"&gt;32:27&lt;/a&gt;- Building the main menu&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=vSEcBiyNe8Y&amp;amp;t=2328s" rel="noopener noreferrer"&gt;38:48&lt;/a&gt;- Building a CMSLink component&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=vSEcBiyNe8Y&amp;amp;t=2905s" rel="noopener noreferrer"&gt;48:25&lt;/a&gt;- The animated "Let's Talk" SVG call to action&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=vSEcBiyNe8Y&amp;amp;t=3335s" rel="noopener noreferrer"&gt;55:35&lt;/a&gt;- Building the Footer&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=vSEcBiyNe8Y&amp;amp;t=4446s" rel="noopener noreferrer"&gt;1:14:06&lt;/a&gt;- Outro&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use Payload for free
&lt;/h3&gt;

&lt;p&gt;Check out Payload for free, forever. The &lt;a href="https://www.youtube.com/pricing" rel="noopener noreferrer"&gt;Personal License&lt;/a&gt; gives you everything Payload does, all for free. We think you'll find Payload's developer experience second to none. It just feels &lt;em&gt;right&lt;/em&gt; to build sites and apps with it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feedback welcome
&lt;/h3&gt;

&lt;p&gt;As always, let us know what you think about the video - and about Payload!&lt;/p&gt;

</description>
      <category>react</category>
      <category>css</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building a Professionally Designed Website Episode 2 - CSS Framework, Baseline Grid, NextJS Structure</title>
      <dc:creator>James Mikrut</dc:creator>
      <pubDate>Tue, 20 Apr 2021 14:38:04 +0000</pubDate>
      <link>https://forem.com/payloadcms/building-a-professionally-designed-website-episode-2-css-framework-baseline-grid-nextjs-structure-29ci</link>
      <guid>https://forem.com/payloadcms/building-a-professionally-designed-website-episode-2-css-framework-baseline-grid-nextjs-structure-29ci</guid>
      <description>&lt;h3&gt;
  
  
  This is Episode 2 in a series that takes you through how to build a high-end website from a design agency's perspective using &lt;a href="https://payloadcms.com" rel="noopener noreferrer"&gt;Payload CMS&lt;/a&gt;, TypeScript, and statically rendered NextJS.
&lt;/h3&gt;

&lt;p&gt;If you missed the first episode, I'd highly recommend going back and watching that one before jumping in here. In that first episode, we covered a lot of ground, including the setup of the entire Payload CMS needed to drive the content of the website. &lt;/p&gt;

&lt;p&gt;In that first episode, we defined all &lt;a href="https://payloadcms.com/docs/configuration/collections" rel="noopener noreferrer"&gt;Collections&lt;/a&gt; and &lt;a href="https://payloadcms.com/docs/configuration/globals" rel="noopener noreferrer"&gt;Globals&lt;/a&gt; necessary as well as went in-depth with layout-building &lt;a href="https://payloadcms.com/docs/fields/blocks" rel="noopener noreferrer"&gt;Block&lt;/a&gt; definitions.&lt;/p&gt;

&lt;p&gt;Now, in Episode 2, we're ready to start work on the frontend. In this episode, we dive into how to build a lightweight but powerful CSS framework that we'll rely on through the entire site. We'll define breakpoints, media queries, typography, colors, a global reset, and finally - a really awesome way to build a baseline grid using &lt;code&gt;rem&lt;/code&gt;s.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Table of Contents
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=N8PCZxJlz5w?t=0s" rel="noopener noreferrer"&gt;0:00​​&lt;/a&gt; - Intro&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=N8PCZxJlz5w?t=121s" rel="noopener noreferrer"&gt;2:01​​&lt;/a&gt; - React JSS + NextJS setup&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=N8PCZxJlz5w?t=267s" rel="noopener noreferrer"&gt;4:27​​&lt;/a&gt; - Writing our first global styles&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=N8PCZxJlz5w?t=627s" rel="noopener noreferrer"&gt;10:27​​&lt;/a&gt; - The baseline grid - in CSS rems&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=N8PCZxJlz5w?t=1030s" rel="noopener noreferrer"&gt;17:10​​&lt;/a&gt; - Reusable values (breakpoints, colors)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=N8PCZxJlz5w?t=1117s" rel="noopener noreferrer"&gt;19:37​&lt;/a&gt; - Typography&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=N8PCZxJlz5w?t=1368s" rel="noopener noreferrer"&gt;22:48​&lt;/a&gt; - CSS media queries&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=N8PCZxJlz5w?t=1644s" rel="noopener noreferrer"&gt;27:24​&lt;/a&gt; - Installing dependencies (CSS grid, Modal, Window Info)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=N8PCZxJlz5w?t=2115s" rel="noopener noreferrer"&gt;35:15​​&lt;/a&gt; - First component - GridContainer&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=N8PCZxJlz5w?t=2567s" rel="noopener noreferrer"&gt;42:47​​&lt;/a&gt; - Testing our grid, discussing NextJS page folder&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=N8PCZxJlz5w?t=2868s" rel="noopener noreferrer"&gt;47:48​​&lt;/a&gt; - Stubbing out the Template, Header and Footer components&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=N8PCZxJlz5w?t=3322s" rel="noopener noreferrer"&gt;55:22​&lt;/a&gt; - Outro&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Give Payload a shot
&lt;/h3&gt;

&lt;p&gt;If you're a JS / TS developer and haven't heard of &lt;a href="https://payloadcms.com" rel="noopener noreferrer"&gt;Payload&lt;/a&gt; yet, you should give it a shot. Its developer experience is second to none and it just feels &lt;em&gt;right&lt;/em&gt; to build sites and apps with it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feedback welcome
&lt;/h3&gt;

&lt;p&gt;As always, let us know what you think about the video - and about Payload! &lt;/p&gt;

</description>
      <category>typescript</category>
      <category>nextjs</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Building a Professionally Designed Website with NextJS, TypeScript, and Payload CMS - Ep. 1</title>
      <dc:creator>James Mikrut</dc:creator>
      <pubDate>Tue, 23 Mar 2021 14:51:03 +0000</pubDate>
      <link>https://forem.com/payloadcms/building-a-professionally-designed-website-with-nextjs-typescript-and-payload-cms-ep-1-3n8p</link>
      <guid>https://forem.com/payloadcms/building-a-professionally-designed-website-with-nextjs-typescript-and-payload-cms-ep-1-3n8p</guid>
      <description>&lt;h3&gt;
  
  
  Have you ever wondered how professional design firms go about building high-end websites? If so, this series is for you.
&lt;/h3&gt;

&lt;p&gt;Hi, my name is James. Outside of co-founding &lt;a href="https://payloadcms.com" rel="noopener noreferrer"&gt;Payload CMS&lt;/a&gt;, I also operate a professional design firm called &lt;a href="https://trbl.design" rel="noopener noreferrer"&gt;TRBL&lt;/a&gt;. There, we specialize in custom UX and full-stack development—usually TypeScript front-to-back. I've been a designer and full-stack developer for around 10 years now and self-employed for six. I absolutely love the work that I do.&lt;/p&gt;

&lt;p&gt;I have been working on a new video series that describes, in detail, how professional design firms like TRBL go about building completely custom websites using a headless CMS, React, and TypeScript. This series is going to be a bit different than the typical to-do app style videos and I want it to show a much deeper level of project approach and code mentality—all the way from how to scaffold centralized, reusable TypeScript types to how to build websites with React layout-building components.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Table of Contents
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=bxWsZTtqs80&amp;amp;t=0s" rel="noopener noreferrer"&gt;0:00&lt;/a&gt; - Intro&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=bxWsZTtqs80&amp;amp;t=196s" rel="noopener noreferrer"&gt;3:16&lt;/a&gt;​ - Project plan &amp;amp; series overview&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=bxWsZTtqs80&amp;amp;t=926s" rel="noopener noreferrer"&gt;15:26​&lt;/a&gt; - Design review&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=bxWsZTtqs80&amp;amp;t=1517s" rel="noopener noreferrer"&gt;25:17&lt;/a&gt;​ - Payload introduction&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=bxWsZTtqs80&amp;amp;t=2005s" rel="noopener noreferrer"&gt;33:25&lt;/a&gt;​ - Payload + NextJS boilerplate setup&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=bxWsZTtqs80&amp;amp;t=2597s" rel="noopener noreferrer"&gt;43:17​&lt;/a&gt; - CMS scaffolding (form submissions, studies, shared Payload fields, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=bxWsZTtqs80&amp;amp;t=3608s" rel="noopener noreferrer"&gt;1:00:08&lt;/a&gt;​ - The first layout building block - Content&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=bxWsZTtqs80&amp;amp;t=4565s" rel="noopener noreferrer"&gt;1:16:05&lt;/a&gt;​ - Globals configuration (mega menu, footer, social media)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=bxWsZTtqs80&amp;amp;t=5753s" rel="noopener noreferrer"&gt;1:35:53&lt;/a&gt;​ - All layout blocks - fast-forward and review&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=bxWsZTtqs80&amp;amp;t=6409s" rel="noopener noreferrer"&gt;1:46:49​&lt;/a&gt; - Recap&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Series Goals
&lt;/h3&gt;

&lt;p&gt;One of my goals with this series is to provide a window into how professional design firms go about delivering sites with a lot of value to clients that absolutely need that level of quality, and are willing to pay for it. The site that we'll be building is actual, real-world work, and although it's more of a small-to-medium budget for us, the principles involved can be used to deliver websites where clients will happily seek out talented designers / developers and pay $100K+, because they need it.&lt;/p&gt;

&lt;p&gt;Another goal of mine is that I want to challenge our industry to find the clients that value the work that we do, and I want developers to stop selling themselves short. Also, don't worry about the Squarespaces of the world. &lt;strong&gt;Find the budgets that reward you for your skill.&lt;/strong&gt; There is a segment of the market that still absolutely requires high-end development standards and approaches, and it's alive and well.&lt;/p&gt;

&lt;h4&gt;
  
  
  Feedback is Appreciated
&lt;/h4&gt;

&lt;p&gt;This is episode 1 - and it's my first ever YouTube video. I would greatly appreciate any candid feedback that this community has for me, and if you haven't already, please give Payload a shot. We'd love to hear your thoughts.&lt;/p&gt;

&lt;p&gt;Let us know what you think, and thank you!&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>nextjs</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>NextJS, Payload, and TypeScript in a Single Express Server Boilerplate</title>
      <dc:creator>James Mikrut</dc:creator>
      <pubDate>Tue, 09 Feb 2021 14:49:30 +0000</pubDate>
      <link>https://forem.com/payloadcms/nextjs-payload-and-typescript-in-a-single-express-server-boilerplate-3oe2</link>
      <guid>https://forem.com/payloadcms/nextjs-payload-and-typescript-in-a-single-express-server-boilerplate-3oe2</guid>
      <description>&lt;p&gt;One of Payload CMS’ core ideologies is that it does not impose any of its own structure on you as a developer. You give it an Express server to use—not the other way around, and this pays dividends in developer freedom and developer experience.&lt;/p&gt;

&lt;p&gt;An example of how this approach can be leveraged is by running a full NextJS site on the same Express app as your Payload CMS instance. We’ve built a boilerplate that demonstrates exactly how this works:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check it out:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/payloadcms/nextjs-custom-server" rel="noopener noreferrer"&gt;https://github.com/payloadcms/nextjs-custom-server&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This boilerplate includes the following:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Payload CMS and NextJS up and running on a single Express server&lt;/li&gt;
&lt;li&gt;Super fast &lt;a href="https://payloadcms.com/docs/local-api/overview" rel="noopener noreferrer"&gt;Local API&lt;/a&gt; usage within pages’ &lt;code&gt;getServerSideProps&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A demonstration for how to use TypeScript in a Payload and NextJS project&lt;/li&gt;
&lt;li&gt;Example code for how Payload’s &lt;a href="https://payloadcms.com/docs/fields/blocks" rel="noopener noreferrer"&gt;Blocks field type&lt;/a&gt; can be leveraged to produce dynamic, layout-builder style pages&lt;/li&gt;
&lt;li&gt;Page meta data using NextJS’ &lt;code&gt;Head&lt;/code&gt; component&lt;/li&gt;
&lt;li&gt;Payload’s &lt;a href="https://payloadcms.com/docs/upload/overview" rel="noopener noreferrer"&gt;Upload&lt;/a&gt; support, including automatic image resizing&lt;/li&gt;
&lt;li&gt;How Payload’s &lt;a href="https://payloadcms.com/docs/local-api/overview" rel="noopener noreferrer"&gt;Local API&lt;/a&gt; can be used to seed initial data into your database&lt;/li&gt;
&lt;li&gt;How Payload’s &lt;a href="https://payloadcms.com/docs/fields/rich-text" rel="noopener noreferrer"&gt;Rich Text field&lt;/a&gt; can be used to &lt;a href="https://github.com/payloadcms/nextjs-custom-server/blob/master/components/RichText/serialize.tsx" rel="noopener noreferrer"&gt;map 1:1 to React components&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://trbl.design" rel="noopener noreferrer"&gt;TRBL’s&lt;/a&gt; ESLint config set up and ready to go&lt;/li&gt;
&lt;li&gt;Environment variables properly and securely configured using dotenv&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When this type of setup is best used&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you know you need a CMS and will be leveraging NextJS in a server-side rendering capacity, and know that you will not be deploying on Vercel, this boilerplate will be perfect for you. This approach can be super valuable and can get you up and running with a full CMS—complete with everything you need to build a modern, blazingly fast site or app including custom validation, full &lt;a href="https://payloadcms.com/docs/authentication/overview" rel="noopener noreferrer"&gt;authentication&lt;/a&gt;, &lt;a href="https://payloadcms.com/docs/access-control/overview" rel="noopener noreferrer"&gt;access control&lt;/a&gt;, and much more.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring TypeScript
&lt;/h3&gt;

&lt;p&gt;Much of the complexity that we handle within this boilerplate comes from using TypeScript to build a custom NextJS server. At Payload, we’re big fans of TypeScript (all of Payload is written in TS). We’re doing our best to adopt and embrace it completely, and we think that it’s only going to get more and more popular.&lt;/p&gt;

&lt;p&gt;This boilerplate contains two &lt;code&gt;tsconfig.json&lt;/code&gt; files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The main &lt;code&gt;tsconfig.json&lt;/code&gt;, which will be used for the entirety of your NextJS app, including all your React components&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;tsconfig.server.json&lt;/code&gt; file, which will handle everything in the &lt;code&gt;/server&lt;/code&gt; folder&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll see that we’ve extended the main &lt;code&gt;tsconfig.json&lt;/code&gt; config within the server config and overridden a few properties.&lt;/p&gt;

&lt;p&gt;Due to how NextJS relies on dynamic import statements, it requires that its TypeScript projects specify &lt;code&gt;"module": "esnext"&lt;/code&gt; in their TS configs. But, Express requires the CommonJS pattern — meaning we have no choice but to require two separate TS configs. No big deal, but this is a common “gotcha” when working with NextJS and TypeScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up the server
&lt;/h3&gt;

&lt;p&gt;The Express server itself is pretty simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="cm"&gt;/* eslint-disable global-require */&lt;/span&gt;
&lt;span class="cm"&gt;/* eslint-disable no-console */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;nextBuild&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/dist/build&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;payload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;dotenv&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../.env&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="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PAYLOAD_PUBLIC_SERVER_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SERVER_URL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_SERVER_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SERVER_URL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;license&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PAYLOAD_LICENSE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PAYLOAD_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;mongoURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MONGO_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;express&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_BUILD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;dev&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nextApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRequestHandler&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;nextHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="nx"&gt;nextApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NextJS started&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Server listening on &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NextJS is now building...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;nextBuild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&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;p&gt;First, we load &lt;code&gt;dotenv&lt;/code&gt; and then we expose our &lt;code&gt;SERVER_URL&lt;/code&gt; to both NextJS and Payload. Prefixing environment variables with &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; will ensure that the variable is accessible within NextJS components, and similarly, prefixing a variable with &lt;code&gt;PAYLOAD_PUBLIC_&lt;/code&gt; will expose the variable to Payload’s admin panel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Only expose environment variables like this if you know that they are 100% safe to be readable by the public. For more information about how Payload allows you to expose environment variables to its Admin panel, &lt;a href="https://payloadcms.com/docs/configuration/overview#using-environment-variables-in-your-config" rel="noopener noreferrer"&gt;click here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On line 20, we then &lt;a href="https://payloadcms.com/docs/getting-started/installation#server" rel="noopener noreferrer"&gt;initialize Payload&lt;/a&gt; by passing it a license key (&lt;a href="https://payloadcms.com/docs/production/licensing" rel="noopener noreferrer"&gt;only necessary in production&lt;/a&gt;), a long and unguessable secret string used to secure Payload, a URL pointing to our MongoDB instance, and our newly instantiated Express app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Payload stores your data in MongoDB — so to use Payload, you need to make sure that you have MongoDB up and running either locally or with a third-party platform like MongoDB Atlas.&lt;/p&gt;

&lt;h5&gt;
  
  
  Serving your app vs. building it
&lt;/h5&gt;

&lt;p&gt;On line 27, we perform different actions based on if the &lt;code&gt;NEXT_BUILD&lt;/code&gt; environment variable is set. We do this as a nice-to-have because your Next app is going to be relying on your Payload APIs, especially if it has any static page generation to do. When you go to build your Next app, you probably also need your Payload server to be running.&lt;/p&gt;

&lt;p&gt;So, if the &lt;code&gt;NEXT_BUILD&lt;/code&gt; variable is set, we start up your Express server for you before allowing Next to build. If it’s unset, we just go ahead and prepare the Next app as usual—then fire up the Express server. Easy peasy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layout Building with Blocks
&lt;/h3&gt;

&lt;p&gt;Payload comes with extremely versatile field types that allow you to model any type of data that you need. One of the most capable types is the &lt;a href="https://payloadcms.com/docs/fields/blocks" rel="noopener noreferrer"&gt;Block field&lt;/a&gt; — and with it, you can allow your content editors to build completely dynamic page layouts with a super streamlined interface right within the &lt;a href="https://payloadcms.com/docs/admin/overview" rel="noopener noreferrer"&gt;Payload admin panel&lt;/a&gt;. Admins can then add, remove, and reorder blocks easily based on predefined components that you provide them with.&lt;/p&gt;

&lt;p&gt;The beauty of using a JavaScript library like React together with your Payload API means that you can write React components that map 1:1 with your blocks’ data. Your React components can accept the data that your editors author as props, and boom—your layouts are extremely well-organized and extensible well into the future.&lt;/p&gt;

&lt;p&gt;In this boilerplate, we’ve pictured how you can even write your Payload block configs directly in the same file as their React component counterparts. You could even go so far as to re-use your frontend website’s React component that shows the data saved within Payload’s admin panel itself to edit that same data. There is a ton of potential here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For example, check out the &lt;a href="https://github.com/payloadcms/nextjs-custom-server/blob/master/blocks/CallToAction/index.tsx" rel="noopener noreferrer"&gt;Call to Action block&lt;/a&gt; in this repo.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In that one file, we define the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reusable TypeScript types that correspond with the data within the Block&lt;/li&gt;
&lt;li&gt;A reusable function to be used with Payload’s &lt;a href="https://payloadcms.com/docs/fields/overview#conditional-logic" rel="noopener noreferrer"&gt;Field Conditional Logic&lt;/a&gt; to dynamically show and hide fields based on what type of button is selected (&lt;code&gt;custom&lt;/code&gt; or &lt;code&gt;page&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The Block config itself, describing the fields that are contained within the block. This will be passed to Payload and is the core “definition” of the block&lt;/li&gt;
&lt;li&gt;The React component to be used on the frontend NextJS site to render the &lt;code&gt;CallToAction&lt;/code&gt; block itself&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;These things don’t all need to be in the same file, but if you want to, Payload allows for it.&lt;/strong&gt; Both NextJS and Payload support transpiling JSX within their files. You should be able to write your projects however you want.&lt;/p&gt;

&lt;p&gt;Here’s how that &lt;code&gt;CallToAction&lt;/code&gt; block looks in the Admin panel:&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%2Fi%2F1egpe330ssq75gs02a74.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%2Fi%2F1egpe330ssq75gs02a74.png" alt="Payload Admin Panel" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here’s how it looks in the minimally styled NextJS frontend:&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%2Fi%2Fllqa8gxe3gdjedl7fcm1.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%2Fi%2Fllqa8gxe3gdjedl7fcm1.jpg" alt="Layout Building Blocks in React and NextJS" width="800" height="560"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamically rendering Blocks in React
&lt;/h3&gt;

&lt;p&gt;Actually going to render the blocks themselves in React is also pretty trivial:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/components/RenderBlocks/index.tsx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Layout&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../collections/Page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;components&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../blocks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;classes&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./index.module.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Layout&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RenderBlocks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{[&lt;/span&gt;
    &lt;span class="nx"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;renderBlocks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;blockType&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;section&lt;/span&gt;
            &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Block&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/section&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;})}&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;RenderBlocks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above component accepts a &lt;code&gt;layout&lt;/code&gt; prop that is typed to an array of Payload blocks. The component then maps over the provided blocks and selects a block from those provided by the &lt;code&gt;blockType&lt;/code&gt; of each block in the array. Props are provided, and the block is rendered! Beautiful. So simple, and so much power.&lt;/p&gt;

&lt;h3&gt;
  
  
  Seeding data using Payload’s Local API
&lt;/h3&gt;

&lt;p&gt;This boilerplate comes with an optional seed script which can be run via &lt;code&gt;yarn seed&lt;/code&gt; or &lt;code&gt;npm run seed&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It automatically creates one Media document (which uploads and formats a JPG) and two sample Page documents that demonstrate a few Blocks in action.&lt;/p&gt;

&lt;p&gt;Payload’s &lt;a href="https://payloadcms.com/docs/local-api/overview" rel="noopener noreferrer"&gt;Local API&lt;/a&gt; is extremely powerful. It’s got a ton of use cases—including retrieving documents directly on the server within custom routes or within NextJS’ &lt;code&gt;getServerSideProps&lt;/code&gt; as seen in the &lt;a href="https://github.com/payloadcms/nextjs-custom-server/blob/master/pages/%5B...slug%5D.tsx" rel="noopener noreferrer"&gt;Page component&lt;/a&gt; within this boilerplate. It’s super fast, because there’s no HTTP layer: it’s not a typical REST API call or a GraphQL query. It never leaves your server and returns results in a handful of milliseconds, and it’s even faster if you’re running a local MongoDB instance. &lt;strong&gt;You thought NextJS server-rendering was fast? Try it when you don’t even need to leave your server to get your data.&lt;/strong&gt; That’s fast.&lt;/p&gt;

&lt;p&gt;You can also use the Local API &lt;em&gt;completely separately from your running server&lt;/em&gt; within separate Node scripts.&lt;/p&gt;

&lt;p&gt;By passing &lt;code&gt;local: true&lt;/code&gt; to Payload’s &lt;code&gt;init()&lt;/code&gt; call, Payload will skip setting up the REST and GraphQL APIs and only expose its Local API operations. Perfect for seed scripts and similar programmatic activities like batch-sending emails to customers, migrating your data from one shape to another, manually syncing Customer records to a CRM, etc.&lt;/p&gt;

&lt;p&gt;Here’s the seed script that comes with this boilerplate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;payload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;home&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./home.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sample&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./sample.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PAYLOAD_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MONGO_URL&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PAYLOAD_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;mongoURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MONGO_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;local&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="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;seedInitialData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createdMedia&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;media&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Payload&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;filePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./payload.jpg&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createdSamplePage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;homeString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;home&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{{IMAGE_ID}}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createdMedia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{{SAMPLE_PAGE_ID}}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createdSamplePage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;homeString&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Seed completed!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nf"&gt;seedInitialData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pretty awesome stuff.&lt;/p&gt;

&lt;h4&gt;
  
  
  When this boilerplate should not be used
&lt;/h4&gt;

&lt;p&gt;If you plan to &lt;code&gt;next export&lt;/code&gt; a fully static version of your NextJS site, then the value of this boilerplate diminishes a bit and you should likely keep your front + backend entirely separate from each other. In this case, the only strength that this approach offers is that you can host your CMS and your app itself on one server, with one deployment. If possible, in this case, you might want to consider deploying your statically exported site on a CDN-friendly host like Netlify, Vercel, or even an S3 bucket, and host your Payload instance on DigitalOcean, Heroku, or similar.&lt;/p&gt;

&lt;h4&gt;
  
  
  More examples are on their way
&lt;/h4&gt;

&lt;p&gt;We plan to release many more boilerplates in the future, so if this one doesn’t make sense for your needs, make sure to follow along with us to keep up with everything we’ve got coming out, including examples for how to use Payload alongside of a fully static site exported with Next, built with Gatsby, or other similar tactics.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s next?
&lt;/h3&gt;

&lt;p&gt;With this boilerplate, you can build fully featured NextJS sites and apps fully powered by a CMS. So get building! Define your own &lt;a href="https://payloadcms.com/docs/config/collections" rel="noopener noreferrer"&gt;Collections&lt;/a&gt; that describe the shape of your data, make use of Payload’s &lt;a href="https://payloadcms.com/docs/config/globals" rel="noopener noreferrer"&gt;Globals&lt;/a&gt; for items such as header and footer nav structures, or build a full user-authenticated app by relying on Payload’s extensible &lt;a href="https://payloadcms.com/docs/authentication/overview" rel="noopener noreferrer"&gt;Authentication&lt;/a&gt; support.&lt;/p&gt;

&lt;p&gt;If you’d rather start a blank Payload project, you can get started in one line:&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;



&lt;p&gt;From there, you’ll be prompted to choose between a few different starter templates in JS or TS.&lt;/p&gt;

&lt;p&gt;It’s also super easy to &lt;a href="https://payloadcms.com/docs/getting-started/installation#from-scratch" rel="noopener noreferrer"&gt;build out a Payload project yourself from scratch&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let us know what you think
&lt;/h3&gt;

&lt;p&gt;We want Payload to be the best CMS out there for modern JavaScript developers. Since our launch, we’ve received amazing feedback on our ideas and have had an awesome reception from the community, but we’re only getting started. We’d love to hear what you think. Leave a comment here with your thoughts, submit any issues or feature requests you might come across on our &lt;a href="https://github.com/payloadcms/payload" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;, or &lt;a href="//mailto:info@payloadcms.com"&gt;send us an email&lt;/a&gt;. We happily give out pro-bono licenses to open-source projects and nonprofits, as well as personal projects on a case-by-case basis. If this is you, let us know!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thank you for reading and keep an eye out for more!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>nextjs</category>
      <category>javascript</category>
      <category>node</category>
    </item>
  </channel>
</rss>
