<?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>DEV Community: Jacob Evans</title>
    <description>The latest articles on DEV Community by Jacob Evans (@jacobmgevans).</description>
    <link>https://dev.to/jacobmgevans</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%2F37923%2Ff8aba48c-44db-44a1-bc08-12dc9d947d36.png</url>
      <title>DEV Community: Jacob Evans</title>
      <link>https://dev.to/jacobmgevans</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jacobmgevans"/>
    <language>en</language>
    <item>
      <title>Mentorship To Me</title>
      <dc:creator>Jacob Evans</dc:creator>
      <pubDate>Thu, 17 Apr 2025 22:41:12 +0000</pubDate>
      <link>https://dev.to/jacobmgevans/mentorship-to-me-fg7</link>
      <guid>https://dev.to/jacobmgevans/mentorship-to-me-fg7</guid>
      <description>&lt;p&gt;I want to start off by saying, I think traditional mentorship has its place and usefulness, but full disclosure, I have a negative bias.&lt;br&gt;
Here's the thing, mentorship doesn't have to be this formal arrangement where someone wise bestows knowledge upon you from on high. That model? It's broken for most people.&lt;br&gt;
My most valuable "mentors" weren't people who signed up for the job. They were folks in communities like Cloudflare Workers Discord, T3, Theo's, Kent C Dodd's, and React circles who just... showed up consistently. Those who shared their knowledge openly without gatekeeping and those who called me on my BS when I needed it.&lt;br&gt;
The best mentorship experiences I've had came from casual convos in Discord at 2 AM, debugging weird TypeScript errors, or pair programming on random OSS issues during Hacktoberfest. It was getting detailed PR reviews that made me a better dev, not just scheduled coffee chats.&lt;br&gt;
When I was transitioning from military to tech, what helped wasn't finding ONE perfect guide; it was tapping into MANY sources of wisdom and building a network of peers who were just slightly ahead of where I was.&lt;br&gt;
That's why I'm such a big advocate for jumping into open source. Want mentorship? Start contributing to projects you use. The feedback you'll get on PRs is pure gold. The maintainers aren't your "mentors" officially, but damn if they won't level up your skills faster than any formal program.&lt;br&gt;
This isn't to say structured mentorship programs are worthless. They work great for some people! But if you're struggling to find "a mentor," stop looking for that ONE person and start embedding yourself in communities where knowledge flows freely.&lt;br&gt;
Being part of Cloudflare, T3 ecosystem, Theo's, Kent C Dodd's, and the various dev communities I'm in has taught me that the best mentorship is often ambient, it's absorbing knowledge from many sources rather than one guru.&lt;br&gt;
So yeah, traditional mentorship has its place, but don't sleep on the power of community-based learning and peer networks. Sometimes the best guidance comes from the people right next to you in the trenches.&lt;br&gt;
What's been your experience? Hit me up, always curious to hear different perspectives on this!&lt;/p&gt;

</description>
      <category>career</category>
      <category>motivation</category>
      <category>mentor</category>
    </item>
    <item>
      <title>Mathematical Magic with JS Sets: Demystifying</title>
      <dc:creator>Jacob Evans</dc:creator>
      <pubDate>Sat, 08 Feb 2025 00:01:54 +0000</pubDate>
      <link>https://dev.to/jacobmgevans/mathematical-magic-with-js-sets-demystifying-41g4</link>
      <guid>https://dev.to/jacobmgevans/mathematical-magic-with-js-sets-demystifying-41g4</guid>
      <description>&lt;p&gt;If you’re as enamored with the elegance of mathematics as you are with clean, efficient code, you’re in for a treat. Today, we’re taking a deep-ish dive into JavaScript’s &lt;code&gt;Set&lt;/code&gt; not just as a tool for eliminating duplicates, but as a useful tool for implementing classical mathematical set theory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Sets?
&lt;/h2&gt;

&lt;p&gt;In mathematics, a set is a collection of distinct elements. This concept translates almost perfectly to JavaScript’s &lt;code&gt;Set&lt;/code&gt; object. Instead of wrangling arrays and manually filtering duplicates, a &lt;code&gt;Set&lt;/code&gt; gives you uniqueness by default. This property makes it an ideal data structure for modeling everything from simple collections to more complex mathematical operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set Theory in Code
&lt;/h2&gt;

&lt;p&gt;Let’s explore some canonical set operations union, intersection, difference, and symmetric difference, and see how we can bring mathematical theory to life with JavaScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  Union
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;union&lt;/strong&gt; of two sets (A) and (B) is the set of elements that are in (A), in (B), or in both. With JavaScript sets, merging these collections is as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;union&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setB&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;setA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;setB&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Example&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;setA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&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;setB&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&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="nf"&gt;union&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setB&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Set { 1, 2, 3, 4, 5 }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spread both sets into an array, and let the &lt;code&gt;Set&lt;/code&gt; constructor do its magic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Intersection
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;intersection&lt;/strong&gt; of sets (A) and (B) consists of elements common to both. Implementing this operation can be done by filtering one set’s values against the other:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;intersection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setB&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;setA&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="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&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="nf"&gt;intersection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setB&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Set { 3 }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach captures the essence of finding common ground between two collections.&lt;/p&gt;

&lt;h3&gt;
  
  
  Difference
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;difference&lt;/strong&gt; (A - B) yields the elements present in (A) but not in (B). This operation is particularly handy when you want to subtract one set from another:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;difference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setB&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;setA&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="nx"&gt;item&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;setB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&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="nf"&gt;difference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setB&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Set { 1, 2 }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Symmetric Difference
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;symmetric difference&lt;/strong&gt; is the set of elements that are in either (A) or (B) but not in both. Essentially, it’s the union of the sets minus their intersection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;symmetricDifference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setB&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;unionSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;union&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setB&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;intersectionSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;intersection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setB&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;unionSet&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="nx"&gt;item&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;intersectionSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&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="nf"&gt;symmetricDifference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setB&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Set { 1, 2, 4, 5 }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Beyond Basic Operations
&lt;/h3&gt;

&lt;p&gt;While the union, intersection, difference, and symmetric difference form the backbone of set theory, the potential of JavaScript’s &lt;code&gt;Set&lt;/code&gt; doesn’t stop there. You can leverage sets in graph algorithms, implement membership checks for large datasets, or even model more abstract mathematical structures. For instance, if you're tackling a problem like detecting cycles in a graph, using a set to track visited nodes can simplify your logic and improve performance; which can be a huge win!&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance Insights
&lt;/h3&gt;

&lt;p&gt;Remember, operations like &lt;code&gt;add&lt;/code&gt;, &lt;code&gt;delete&lt;/code&gt;, and &lt;code&gt;has&lt;/code&gt; on a &lt;code&gt;Set&lt;/code&gt; are typically O(1), making them extremely efficient. However, operations that involve iterating over the set—such as our union or intersection functions scale linearly with the number of elements (sad but can't be avoided). In most cases, this trade-off is more than acceptable given the clarity and conciseness you gain (until Quantum computers take over and O complexity now longer matters lol).&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up...
&lt;/h2&gt;

&lt;p&gt;JavaScript’s &lt;code&gt;Set&lt;/code&gt; object isn’t just a data structure; it’s a bridge between abstract mathematical concepts and real-world programming challenges. By leveraging sets to perform operations that are fundamental to mathematics, we can write code that is not only efficient but also elegant and expressive.&lt;/p&gt;

&lt;p&gt;I hope this exploration inspires you to think of sets as more than just a tool for managing collections. Next time you face a problem involving unique elements or need to implement a classic set operation, remember! Sometimes, the simplest solution is the most mathematically beautiful.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;




&lt;p&gt;Follow me on &lt;a href="https://twitter.com/jacobmgevans" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or check out my &lt;a href="https://github.com/jacobmgevans" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; for more deep dives and practical insights&lt;/p&gt;

</description>
      <category>javascript</category>
    </item>
    <item>
      <title>Clerk in 2023: A Year in Review</title>
      <dc:creator>Jacob Evans</dc:creator>
      <pubDate>Thu, 04 Jan 2024 22:48:48 +0000</pubDate>
      <link>https://dev.to/clerk/clerk-in-2023-a-year-in-review-4dc8</link>
      <guid>https://dev.to/clerk/clerk-in-2023-a-year-in-review-4dc8</guid>
      <description>&lt;p&gt;This was a exciting year for Clerk, packed with new features and a growing community around Clerk’s user authentication platform. Let's rewind and celebrate some of the milestones that marked our journey!&lt;/p&gt;

&lt;h2&gt;
  
  
  January
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffuej9moe0j7w4vk8akza.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffuej9moe0j7w4vk8akza.png" alt="T3 &amp;amp; Clerk Logos" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Enhanced DX &amp;amp; Security
&lt;/h3&gt;

&lt;p&gt;As part of Clerk’s mission to revolutionize DX, the focus was on removing roadblocks and streamlining workflows. To enhance security, Clerk implemented several key improvements, including eliminating third-party cookies, which can be used for malicious acts such as cross site scripting. Additionally, Clerk improved API key management for stronger access control; all of this means developers can build more secure applications with ease, using Clerk.&lt;/p&gt;

&lt;h3&gt;
  
  
  Upgraded Dashboard &amp;amp; Documentation
&lt;/h3&gt;

&lt;p&gt;Clerk made it easier for developers to find answers and manage their Clerk integrations with a more intuitive interface and comprehensive documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gatsby SDK &amp;amp; Community RedwoodJS SDK
&lt;/h3&gt;

&lt;p&gt;Expanded Clerk's compatibility with popular web development frameworks, making it even easier for developers to integrate user authentication into their projects. The major thing to highlight here is Clerk’s growing community and passion for enabling Clerk to work with the tools they love! Learn more in this &lt;a href="https://clerk.com/blog/changelog-2023-01-27#gatsby-v-5" rel="noopener noreferrer"&gt;changelog post&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Popular Start Template &lt;code&gt;t3-turbo-and-clerk&lt;/code&gt; Powered by Clerk
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/clerk/t3-turbo-and-clerk" rel="noopener noreferrer"&gt;This open-source template&lt;/a&gt; showcases the power of Clerk for building web and mobile apps with robust authentication.&lt;/p&gt;

&lt;h2&gt;
  
  
  February
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Improved Next.js Middleware Integration
&lt;/h3&gt;

&lt;p&gt;Clerk’s &lt;a href="https://clerk.com/blog/changelog-2023-02-10#next-js-middleware-protection-strategy" rel="noopener noreferrer"&gt;new Middleware&lt;/a&gt; allowed for seamless integration with the popular Next.js framework, allowing developers to easily add user authentication to their Next.js applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fastify &amp;amp; RedwoodJS v4 Integrations
&lt;/h3&gt;

&lt;p&gt;Further broadened Clerk's reach by offering official integrations with &lt;a href="https://clerk.com/docs/quickstarts/fastify#use-clerk-with-fastify" rel="noopener noreferrer"&gt;Fastify&lt;/a&gt; and &lt;a href="https://clerk.com/docs/quickstarts/redwood#use-clerk-with-redwood-js" rel="noopener noreferrer"&gt;RedwoodJS&lt;/a&gt; frameworks, empowering developers to choose the tools they love.&lt;/p&gt;

&lt;h3&gt;
  
  
  Documentation Overhaul
&lt;/h3&gt;

&lt;p&gt;Revamped Clerk's documentation with better organization, search functionality, performance, and authoring experience, making it easier for developers of all levels to learn and use Clerk.&lt;/p&gt;

&lt;h2&gt;
  
  
  March
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff8ahcfpsh1reu9c77241.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff8ahcfpsh1reu9c77241.png" alt="$15M Series" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  $15M Series A Led by Madrona &amp;amp; Acquired Clerk.com Domain!
&lt;/h3&gt;

&lt;p&gt;This significant investment and domain move solidified Clerk's position as a leading authentication provider and fueled Clerk’s continued growth. Read more about our Series A in this &lt;a href="https://clerk.com/blog/series-a" rel="noopener noreferrer"&gt;announcement post&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  SDK Performance Boost through Lazy Loading
&lt;/h3&gt;

&lt;p&gt;Optimized performance by ensuring Clerk's pre-built components only load when needed, preventing initial render time delays and ensuring a smooth user experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dedicated Chrome Extensions SDK
&lt;/h3&gt;

&lt;p&gt;Opened up possibilities for secure user authentication in the booming Chrome extension market. Check out the &lt;a href="https://github.com/clerk/clerk-chrome-extension-starter" rel="noopener noreferrer"&gt;starter repo&lt;/a&gt; on GitHub.&lt;/p&gt;

&lt;h3&gt;
  
  
  T3 Stack Tutorial by Theo Browne Showcases Clerk, Vercel &amp;amp; PlanetScale
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://www.youtube.com/watch?v=YkOSUVzOAA4&amp;amp;t" rel="noopener noreferrer"&gt;T3 Stack Tutorial&lt;/a&gt; highlighted the seamless integration of Clerk with other popular tools like Vercel and PlanetScale, demonstrating how developers can build powerful and scalable applications with a unified tech stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  April
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Clerk Becomes an Identity Provider
&lt;/h3&gt;

&lt;p&gt;Clerk IdP enables large enterprise companies juggling Authentication between all their vendors to create a nexus through Clerk for Single Sign-On functionality.&lt;/p&gt;

&lt;h3&gt;
  
  
  Improved Phone &amp;amp; Email Input, Customizable Hosted Pages through Dashboard
&lt;/h3&gt;

&lt;p&gt;Enhanced the user experience with more intuitive input fields and the ability to personalize the look and feel of login and signup pages, boosting brand consistency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Full Expo 48 Support
&lt;/h3&gt;

&lt;p&gt;Made Clerk a perfect fit for mobile developers using Expo, a popular framework for building cross-platform apps, and streamlined the creation of custom login flows within Expo apps. Learn more in this &lt;a href="https://clerk.com/blog/changelog-2023-04-07#expo-48-support" rel="noopener noreferrer"&gt;changelog entry&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  May
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fet2remvjxabrqur4sjgk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fet2remvjxabrqur4sjgk.png" alt="Digital Fingerprint" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Next.js 13.4 &amp;amp; App Router Support
&lt;/h3&gt;

&lt;p&gt;Clerk swiftly followed the release of Next.js 13.4 and App Router stable by &lt;a href="https://clerk.com/blog/nextjs-13-4" rel="noopener noreferrer"&gt;offering full support&lt;/a&gt; on May 5th. This made Clerk one of the first development tools and the first authentication provider to fully leverage the power of Next.js, React Server Components, and Edge middleware.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enhanced Password Security &amp;amp; UX
&lt;/h3&gt;

&lt;p&gt;May saw a &lt;a href="https://clerk.com/blog/a-new-password-experience" rel="noopener noreferrer"&gt;significant upgrade to password functionalities&lt;/a&gt; in Clerk applications. Setting and resetting passwords became smooth and user-friendly, while breach detection and complexity tests ensured stronger security.&lt;/p&gt;

&lt;p&gt;Developers gained complete control over password security through the Dashboard, allowing them to tailor password policies and requirements to their specific needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stepping into the Public Spotlight
&lt;/h3&gt;

&lt;p&gt;Clerk's presence in the community exploded in May. We transitioned from sporadic YouTube mentions to sponsoring and presenting at major conferences.&lt;/p&gt;

&lt;p&gt;CEO Colin Sidoti's &lt;a href="https://www.youtube.com/watch?v=enUuBY3HXh4" rel="noopener noreferrer"&gt;keynote at Reactathon&lt;/a&gt;, advocating for the power of well-designed components, and VP of Engineering Sokratis Vidros's talk at CityJS on &lt;a href="https://www.youtube.com/watch?v=_ypxvJ0oU-Y" rel="noopener noreferrer"&gt;building for JavaScript edge runtimes&lt;/a&gt;, solidified Clerk's position as a thought leader in the industry.&lt;/p&gt;

&lt;h3&gt;
  
  
  "How We Roll" Launch
&lt;/h3&gt;

&lt;p&gt;May also marked the launch of the &lt;a href="https://clerk.com/blog/how-we-roll-passwords" rel="noopener noreferrer"&gt;"How We Roll" blog series&lt;/a&gt;, offering a behind-the-scenes look at Clerk's technology and how it delivers a seamless user experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  UX Tweaks &amp;amp; Optimization
&lt;/h3&gt;

&lt;p&gt;Recognizing the power of well-crafted UI components, Clerk focused on subtle but impactful UX improvements. Highly customizable user avatars with hover effects and subsequent image size optimizations are prime examples of this dedication to user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  June
&lt;/h2&gt;

&lt;h3&gt;
  
  
  SAML
&lt;/h3&gt;

&lt;p&gt;In June, &lt;a href="https://clerk.com/docs/authentication/saml-at-clerk#saml-at-clerk-beta" rel="noopener noreferrer"&gt;SAML support&lt;/a&gt; was released to public beta, allowing companies to leverage Clerk to seamlessly integrate their internal tools and frontends with their SAML services. Just a few projects leveraging Clerk’s SAML offering include, &lt;a href="https://www.airflip.com/" rel="noopener noreferrer"&gt;Airflip&lt;/a&gt; a modern tool for procurement teams, &lt;a href="https://lawhive.co.uk/" rel="noopener noreferrer"&gt;Lawhive&lt;/a&gt; a powerful platform for finding litigators in the U.K., and &lt;a href="https://clibrain.com/" rel="noopener noreferrer"&gt;Casa&lt;/a&gt; a LLM specialized in usage in Spanish.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bot Detection
&lt;/h3&gt;

&lt;p&gt;Clerk’s ownership of the authentication flows allows &lt;a href="https://clerk.com/blog/changelog-2023-07-07#enhanced-bot-detection-for-ui-components" rel="noopener noreferrer"&gt;enhanced bot detection&lt;/a&gt;. Clerk’s hosted pages were retrofitted with layers of bot detection capabilities in June. Clerk also enabled self-service user delete, an important user privacy feature that every app collecting user data should implement.&lt;/p&gt;

&lt;h2&gt;
  
  
  July
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Bot Detection in the JavaScript SDK
&lt;/h3&gt;

&lt;p&gt;Clerk’s bot detection capabilities were quickly brought to the JavaScript SDK, including the pre-built components and hooks, ensuring that you can be much more confident about your users not being bots. This capability is a must-have for applications providing free trial or usage credits, user generated content, or social interactions, and comes with Clerk out-of-the-box with no additional configuration required.&lt;/p&gt;

&lt;h3&gt;
  
  
  API Improvements for Sorting Users &amp;amp; Organizations
&lt;/h3&gt;

&lt;p&gt;The ownership of user management also comes with a responsibility - user data being stored with Clerk should not become a bottleneck when developing applications, and developers should be able to query the data however they want. July saw improvements to the APIs for querying users and organizations with &lt;a href="https://clerk.com/blog/changelog-2023-07-21#filtering-and-sorting-for-users-organizations-members" rel="noopener noreferrer"&gt;advanced filtering and sorting capabilities&lt;/a&gt;, and better customization of the session token with custom user data.&lt;/p&gt;

&lt;h2&gt;
  
  
  August
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Detection for Disposable Emails &amp;amp; Subaddressing
&lt;/h3&gt;

&lt;p&gt;The defenses put in place to fight the bots got even better in August, when Clerk added &lt;a href="https://clerk.com/docs/authentication/configuration/restrictions#block-email-subaddresses" rel="noopener noreferrer"&gt;detection for disposable emails and subaddressing&lt;/a&gt;. These capabilities make Clerk especially useful to AI products for which bots can be very costly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reverse Proxy Support
&lt;/h3&gt;

&lt;p&gt;Another key infrastructure upgrade quickly followed - support for &lt;a href="https://clerk.com/docs/advanced-usage/using-proxies#proxying-the-clerk-frontend-api" rel="noopener noreferrer"&gt;proxying the requests to Clerk’s Frontend API&lt;/a&gt; through a reverse proxy server!&lt;/p&gt;

&lt;p&gt;The reverse proxy support unlocked an entire realm of capabilities, since your application can now be deployed on any domain (preview, staging, tenant subdomains etc) and have full access to authentication, as well as synchronization of signed in sessions.&lt;/p&gt;

&lt;h3&gt;
  
  
  “How We Roll” Conclusion
&lt;/h3&gt;

&lt;p&gt;The blog series “How We Roll” closed out with its &lt;a href="https://clerk.com/blog/how-we-roll-roundup" rel="noopener noreferrer"&gt;10th chapter&lt;/a&gt;, rounding up prior chapters and summarizing how Clerk maximizes developer experience, application security, and user experience. The community made some amazing contributions to Clerk’s integrations ecosystem through a Vue, Elysia, and Rust integration, fully open source like all of Clerk’s integrations.&lt;/p&gt;

&lt;h2&gt;
  
  
  September
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fokt724ifslaf89tpor0k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fokt724ifslaf89tpor0k.png" alt="Dashboard Account Portal Screenshot" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Account Portal
&lt;/h3&gt;

&lt;p&gt;September saw the release of Clerk’s &lt;a href="https://clerk.com/docs/account-portal/getting-started#getting-started-with-the-account-portal" rel="noopener noreferrer"&gt;Account Portal&lt;/a&gt;, which replaced hosted pages as the fastest way to authenticate any application, by eliminating the need for the developer to build any authentication related UI at all. The Account Portal was a fresh redesign of the entire user experience, along with better control over the look and feel.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom Domain Registration
&lt;/h3&gt;

&lt;p&gt;Clerk’s B2B offerings continue to get crucial upgrades, like custom domain registration for organizations, allowing users to automatically join organization based on their work email instead of requiring an invite from the admins.&lt;/p&gt;

&lt;h3&gt;
  
  
  Form Pre-Fill Ability
&lt;/h3&gt;

&lt;p&gt;The pre-built components for sign in and sign up also received the &lt;a href="https://clerk.com/docs/account-portal/direct-links#prefill-sign-in-and-sign-up-fields" rel="noopener noreferrer"&gt;ability to pre-fill&lt;/a&gt; the form, making them a lot more flexible and elevating the user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  October
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1y18kunoitrejhhau866.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1y18kunoitrejhhau866.png" alt="Feedback Portal Screenshot" width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Changelog &amp;amp; Roadmap
&lt;/h3&gt;

&lt;p&gt;This is getting to be a lot to keep track of, right? We agree, and in October we addressed this through the new changelog and roadmap.&lt;/p&gt;

&lt;p&gt;The new &lt;a href="https://clerk.com/changelog" rel="noopener noreferrer"&gt;changelog page&lt;/a&gt; on clerk.com provides a one stop hub for all the important releases and announcements, while the &lt;a href="https://feedback.clerk.com/roadmap" rel="noopener noreferrer"&gt;roadmap&lt;/a&gt; provides a lot of insight into the ongoing efforts of our product teams and provide any feedback for a new feature or improvements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom Pages
&lt;/h3&gt;

&lt;p&gt;We also want to make sure as developers scale their products and require user management capabilities that Clerk doesn’t provide, they don’t have to opt out of the pre built components and build completely new UI. &lt;a href="https://clerk.com/changelog/2023-10-26" rel="noopener noreferrer"&gt;Custom pages&lt;/a&gt; in user and organization profile take their extensibility to the next level.&lt;/p&gt;

&lt;h2&gt;
  
  
  November
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqalgz7zfdywhdjvll843.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqalgz7zfdywhdjvll843.png" alt="Pricing Page Screenshot" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Pricing Overhaul
&lt;/h3&gt;

&lt;p&gt;We want the experience of Clerk to be available to everyone, including developers who don’t have paying users. We solidified our dedication towards the affordability of Clerk with a massive pricing overhaul, which came with 10,000 free MAUs for everyone, with the user’s first day free, and paid add-ons for the advanced authentication, administration, and B2B features. Read our &lt;a href="https://clerk.com/blog/new-pricing-plans" rel="noopener noreferrer"&gt;announcement post&lt;/a&gt; for further details.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hono Adapter
&lt;/h3&gt;

&lt;p&gt;Applications built using Hono, a powerful and lightweight web server known for its compatibility with edge runtimes, also got access to Clerk’s authentication features with an &lt;a href="https://github.com/honojs/middleware/tree/main/packages/clerk-auth" rel="noopener noreferrer"&gt;official adapter&lt;/a&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  December
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Account Lockout
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://clerk.com/changelog/2023-12-01" rel="noopener noreferrer"&gt;Account Lockout&lt;/a&gt; is a Clerk feature that enables you to protect your users from brute-force attacks on static credentials such as passwords or backup codes. When enabled it tracks all the attempts and locks down the account after 100, the default attempts, it’s locked for an hour!&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom Roles &amp;amp; Permissions
&lt;/h3&gt;

&lt;p&gt;One of our most sought after features of the year, custom roles and permissions, dropped in December, just in time for the holidays. Companies were able to simplify and improve their UX around roles, permissions and access provided to their end users.&lt;/p&gt;

&lt;p&gt;As a part of this addition, we've added helper functions and components – &lt;code&gt;has()&lt;/code&gt;, &lt;code&gt;protect()&lt;/code&gt; and &lt;code&gt;&amp;lt;Protect /&amp;gt;&lt;/code&gt;. To learn more, read our &lt;a href="https://clerk.com/blog/introducing-authorization" rel="noopener noreferrer"&gt;announcement post&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  FAPI Reference Docs
&lt;/h3&gt;

&lt;p&gt;Our &lt;a href="https://clerk.com/docs/reference/frontend-api" rel="noopener noreferrer"&gt;Frontend API documentation&lt;/a&gt; is back and better than ever!&lt;/p&gt;

&lt;h3&gt;
  
  
  Fetch User By Activity
&lt;/h3&gt;

&lt;p&gt;Another drop was a new endpoint that allows insight into when a user was last active, this is great for all kinds of UX or admin dashboard implementations! Check out the &lt;a href="https://clerk.com/changelog/2023-12-07" rel="noopener noreferrer"&gt;changelog entry&lt;/a&gt; to learn more.&lt;/p&gt;

&lt;h2&gt;
  
  
  That's A Wrap!
&lt;/h2&gt;

&lt;p&gt;Let's reach new heights in 2024 with Clerk, don't stop the auth conversation! Dive into the nitty-gritty with our technical wizards on &lt;a href="https://clerk.com/discord" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;. Share your ideas, get expert advice, and join the Clerk community building the future of User Management. Plus, stay in the know with &lt;a href="https://twitter.com/clerkdev" rel="noopener noreferrer"&gt;@ClerkDev&lt;/a&gt; on Twitter for all the latest releases and sneak peeks. Your seamless journey starts here!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Updated Pricing: 10,000 MAUs Free, and a new “Pro Plan”</title>
      <dc:creator>Jacob Evans</dc:creator>
      <pubDate>Thu, 30 Nov 2023 21:58:15 +0000</pubDate>
      <link>https://dev.to/clerk/updated-pricing-10000-maus-free-and-a-new-pro-plan-5gaa</link>
      <guid>https://dev.to/clerk/updated-pricing-10000-maus-free-and-a-new-pro-plan-5gaa</guid>
      <description>&lt;p&gt;We’re thrilled to introduce a simplified pricing structure that’s not only easier to understand, but also significantly cheaper for most applications currently using Clerk. &lt;strong&gt;Every application gets 10,000 free monthly active users (MAUs)!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Brand new users will also now receive their &lt;strong&gt;“First Day Free,”&lt;/strong&gt; which means their activity is not counted until 24 hours after signing up. This ensures you are not charged for users who sign up, but churn within their first day and do not return.&lt;/p&gt;

&lt;p&gt;Since Clerk’s inception, our primary goal has been to lower the barrier to building production-grade applications. Every application deserves secure and seamless user management that takes minutes to setup, not days — freeing you to focus on what’s important: &lt;em&gt;your application&lt;/em&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Existing customers will be grandfathered into their current plans, however, it may be cheaper to switch, so please evaluate accordingly and reach out to us if you have any questions. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Our new simplified pricing structure consists of “Free”, “Pro” and “Enterprise” plans. Where each plan includes your first 10,000 MAUs free and you will never pay for an inactive user. Additional features can be added to the Pro plan via “Pro add-ons” tailor made for specific use cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Free Plan – Up to 10,000 MAUs
&lt;/h2&gt;

&lt;p&gt;Our free plan remains generous, with no features removed. You still get beautifully designed, performant, and customizable sign-in pages on the domain of your choosing. However, we’ve now doubled the included number of MAUs! Get started, and iterate longer — completely for free!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://clerk.com/pricing" rel="noopener noreferrer"&gt;Visit the pricing page to see all features in the Free plan&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Going Pro – A complete feature set for most use cases
&lt;/h2&gt;

&lt;p&gt;The new Pro plan includes the first 10,000 MAUs, and starts at $25/mo. Additional MAUs are only $0.02 each. This is a dramatic simplification, and includes 10X more MAUs compared to the previous Hobby plan. The Pro plan also now includes the ability to set a “Custom Session Duration,” which has been a key sticking point in the past.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Additional Pro plan features include:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Removing Clerk Branding&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Custom Session Duration (&lt;em&gt;previously in the Business plan&lt;/em&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Password Complexity Requirements&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;User Ban/Unban&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Allowlist/Blocklist&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Multiple Domains&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SMS Authentication&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SOC2 Report on Request&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://clerk.com/pricing" rel="noopener noreferrer"&gt;Visit the pricing page to see all features in the Pro plan&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pro Add-Ons – Customize to fit your needs
&lt;/h2&gt;

&lt;p&gt;While the Pro plan is designed for the majority of applications, add-ons are tailored to specific business needs and are priced according to their added value. Today we’re launching with 3 Pro add-ons that will become more feature-rich over time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enhanced Authentication&lt;/strong&gt; — includes MFA&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enhanced Organizations&lt;/strong&gt; — includes domain restrictions, and custom roles and permissions (&lt;em&gt;Stay tuned for this new feature&lt;/em&gt; 😉)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enhanced Administration&lt;/strong&gt; — includes user impersonation&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://clerk.com/pricing" rel="noopener noreferrer"&gt;Visit the pricing page to see all features in each Pro add-on&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Future
&lt;/h2&gt;

&lt;p&gt;We know pricing changes lead to uncertainty and we’re committed to minimizing them. Our end-goal here is to continue to drop the price of authentication as we add value in adjacent areas. Building applications should be getting easier and cheaper over time, and this pricing change is just one step in the right direction. Clerk will always be committed to the developer community, and hopefully this pricing overhaul reflects that.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://clerk.com/support" rel="noopener noreferrer"&gt;We’re here if you have any questions&lt;/a&gt; — Happy building!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>news</category>
      <category>javascript</category>
      <category>startup</category>
    </item>
    <item>
      <title>Next.js Authentication with Clerk: Streamlined SSR Handling</title>
      <dc:creator>Jacob Evans</dc:creator>
      <pubDate>Wed, 22 Nov 2023 15:42:38 +0000</pubDate>
      <link>https://dev.to/clerk/nextjs-authentication-with-clerk-streamlined-ssr-handling-170m</link>
      <guid>https://dev.to/clerk/nextjs-authentication-with-clerk-streamlined-ssr-handling-170m</guid>
      <description>&lt;p&gt;In the ever-evolving world of web development, streamlining tasks is paramount. Clerk, the versatile User Management platform, presents a contemporary way to manage user data in Next.js applications, leaving behind the complexity of old patterns for server-side rendering (SSR). Let's explore the key differences and the value of this new approach with basic code snippets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Navigating Next.js SSR:  The Previous  Approach
&lt;/h2&gt;

&lt;p&gt;Previously, incorporating Clerk in a Next.js app involved intricate setups and token management. Here's a reminder of what the deprecated approach looks like:&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;withServerSideAuth&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;@clerk/nextjs/ssr&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getServerSideProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
&lt;span class="nf"&gt;withServerSideAuth&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;resolvedUrl&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&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;auth&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;sessionId&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="na"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
&lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/sign-in?redirect_url=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;resolvedUrl&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;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The New Way: Streamlined Handling
&lt;/h2&gt;

&lt;p&gt;These are just a few examples of the new streamlined approaches. If you are looking for a more comprehensive breakdown of the best way to read session data in your Next.js app using the Pages Router or App Router, please reference the docs.&lt;/p&gt;

&lt;h3&gt;
  
  
  SSR in App Router – Server Component
&lt;/h3&gt;

&lt;p&gt;The modern Clerk approach in App Router simplifies user data handling with straightforward helper functions. Here's a snippet of the new approach:&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;currentUser&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;@clerk/nextjs&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Example&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;user&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;currentUser&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;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;Hello&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;firstName&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;your&lt;/span&gt; &lt;span class="nx"&gt;ID&lt;/span&gt; &lt;span class="k"&gt;is&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;id&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SSR in Pages Router
&lt;/h3&gt;

&lt;p&gt;If you are using the Pages Router, you can find more detailed examples beyond this basic snippet in the docs.&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;getAuth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;buildClerkProps&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;@clerk/nextjs/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;GetServerSideProps&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;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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getServerSideProps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GetServerSideProps&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="nx"&gt;ctx&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getAuth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&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="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;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// handle user is not logged in.&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Load any data your application needs for&lt;/span&gt;
&lt;span class="c1"&gt;// the page using the userId&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;props&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;buildClerkProps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&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="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Value of the New Approach
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simplicity&lt;/strong&gt;: The new approaches offer a cleaner, more&lt;br&gt;
straightforward codebase for effortless data access.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Efficiency&lt;/strong&gt;: Data access becomes a breeze.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Improved Workflow&lt;/strong&gt;: Focus on building features, not grappling with complex User Management setups.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Maintainability&lt;/strong&gt;: A tidy codebase equals easier maintenance and fewer debugging hassles.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Dive headfirst into Clerk's innovative approach to supercharge your development process, ensuring your apps are always at the cutting edge. This newfound simplicity isn't just for show; it's here to make your work smoother, your code cleaner, and your applications more maintainable. And what's even more exciting? This flexibility isn't confined to one corner of your project; it stretches its arms to Route Handlers as well, making it the perfect fit for the demands of modern web development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ready to Implement Authentication in Your App?
&lt;/h2&gt;

&lt;p&gt;Don't hesitate to explore our Next.js Clerk Docs for a quick and comprehensive guide on integrating Clerk’s User Management into your application within minutes, not days.&lt;/p&gt;

&lt;p&gt;For more in-depth technical inquiries or to engage with our community, feel free to join our &lt;a href="//clerk.com/discord"&gt;Discord&lt;/a&gt;. Stay in the loop with the latest Clerk features, enhancements, and sneak peeks by following our Twitter account, &lt;a href="https://twitter.com/ClerkDev" rel="noopener noreferrer"&gt;@ClerkDev&lt;/a&gt;. Your journey to seamless User Management starts here!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>clerk</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Exploring Clerk Metadata with Stripe Webhooks</title>
      <dc:creator>Jacob Evans</dc:creator>
      <pubDate>Mon, 13 Nov 2023 21:51:00 +0000</pubDate>
      <link>https://dev.to/clerk/exploring-clerk-metadata-with-stripe-webhooks-k04</link>
      <guid>https://dev.to/clerk/exploring-clerk-metadata-with-stripe-webhooks-k04</guid>
      <description>&lt;h2&gt;
  
  
  Introduction to User Metadata
&lt;/h2&gt;

&lt;p&gt;By putting Clerk’s user metadata types to work, developers can proficiently handle user data, making their SaaS integrations run smoother, and work harder. It's like adding a turbocharger to your product's engine, enhancing functionality and improving the user experience, for a more comprehensive, customizable, and synchronizing systems ready to build any SaaS product out there.&lt;/p&gt;

&lt;p&gt;A great feature in the wild world of SaaS product development to power integrations &lt;br&gt;
powering integrations with other powerful products. We're talking about a sturdy, malleable means for handling user data. &lt;/p&gt;

&lt;p&gt;You've got three types of User Metadata –&lt;a href="https://clerk.com/docs/users/metadata#user-metadata" rel="noopener noreferrer"&gt;public, private, and unsafe&lt;/a&gt;. Each one has its own unique access level and use case. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Public&lt;/strong&gt;: It’s an accessible from both the frontend and backend. It's like the town bulletin board where you post things everyone can see but can't change. Think membership levels, user roles, stuff like that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private&lt;/strong&gt;: It’s like the secret stash of user data only reachable from the backend. Perfect for things like account identifiers or subscription details, you know, the stuff you don't want out in the open.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unsafe&lt;/strong&gt;: It might sound a bit ominous, but it is super flexible; treat it like form data and validate any user inputs.
It can be modified and accessed from both frontend and backend. Great for things like user preferences, setting or just any of the nitty-gritty details that make a user's experience unique.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  User Metadata Meets Stripe Webhooks
&lt;/h2&gt;

&lt;p&gt;Harnessing the power of User Metadata in tandem with Stripe’s webhooks offers significant advantages in SaaS product development. Clerk Metadata's flexible user data management paired with Stripe webhooks' real-time transaction updates creates a robust, efficient system. This combination ensures both comprehensive user data handling and prompt responsiveness to transaction events. Utilizing Clerk Metadata alongside Stripe’s webhooks lends itself well for streamlined and user-friendly SaaS development.&lt;/p&gt;

&lt;p&gt;Utilizing Clerk's public User Metadata offers significant advantages for managing user data and transactions in your SaaS product. It allows for real-time updates, such as including a "paid" field after a transaction, offering a clear snapshot of payment statuses. This use of public metadata improves transparency, boosts data management efficiency, and enhances the overall user experience.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tutorial: Implementing Stripe Webhook with Clerk User Metadata
&lt;/h2&gt;

&lt;p&gt;The first step will be setting up accounts at &lt;a href="https://dashboard.clerk.com/sign-up?utm_source=DevRel&amp;amp;utm_medium=blog&amp;amp;utm_campaign=devrel-blog-signups" rel="noopener noreferrer"&gt;Clerk&lt;/a&gt; &amp;amp; &lt;a href="https://dashboard.stripe.com/register" rel="noopener noreferrer"&gt;Stripe&lt;/a&gt;. Once you have those accounts you will follow the well documented &lt;a href="https://clerk.com/docs/quickstarts/nextjs" rel="noopener noreferrer"&gt;Clerk’s Next.js Quickstart Guide&lt;/a&gt;. &lt;br&gt;
To have access to the correct data from the Clerk session you will need to access the &lt;a href="https://clerk.com/docs/backend-requests/making/custom-session-token#click-the-edit-button" rel="noopener noreferrer"&gt;custom session data&lt;/a&gt; on the Dashboard, we will  edit the session data to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;publicMetadata&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="s2"&gt;{{user.public_metadata}}&lt;/span&gt;&lt;span class="dl"&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 last part will be setting up from the Stripe quickstart, the basic &lt;a href="https://stripe.com/docs/webhooks/quickstart" rel="noopener noreferrer"&gt;Stripe webhook&lt;/a&gt;. We will modify it later for our own needs, and there will also be a repo for you to grab afterwards! &lt;br&gt;
By the end of the quickstarts, you should have something in your &lt;code&gt;.env.local&lt;/code&gt;that looks like this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Environment Variables&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# CLERK &lt;/span&gt;
&lt;span class="nv"&gt;NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pk_test_&lt;span class="k"&gt;***&lt;/span&gt;
&lt;span class="nv"&gt;CLERK_SECRET_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sk_test_&lt;span class="k"&gt;***&lt;/span&gt;

&lt;span class="c"&gt;# STRIPE&lt;/span&gt;
&lt;span class="nv"&gt;NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pk_test_&lt;span class="k"&gt;***&lt;/span&gt;
&lt;span class="nv"&gt;STRIPE_SECRET_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sk_test_&lt;span class="k"&gt;***&lt;/span&gt;
&lt;span class="nv"&gt;STRIPE_WEBHOOK_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;whsec_&lt;span class="k"&gt;***&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Auth Middleware Setup
&lt;/h2&gt;

&lt;p&gt;Once we have everything installed we are going to jump into a very basic app, with one private route &lt;code&gt;/members&lt;/code&gt;, and the homepage route will serve as our public route where all the fun stuff will happen. Our &lt;a href="https://clerk.com/docs/references/nextjs/auth-middleware" rel="noopener noreferrer"&gt;Middleware&lt;/a&gt; is going to be handling the access.&lt;/p&gt;

&lt;p&gt;M*&lt;em&gt;iddleware Routes&lt;/em&gt;*&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&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="nf"&gt;authMiddleware&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="c1"&gt;// Making sure homepage route and API, especially the webhook, are both public! &lt;/span&gt;
  &lt;span class="na"&gt;publicRoutes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&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="s2"&gt;/api/(.*)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;afterAuth&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="nx"&gt;auth&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Nice try, you need to sign-in &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;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isPublicRoute&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="nf"&gt;redirectToSignIn&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;returnBackUrl&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;url&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="c1"&gt;// Hey! Members is for members 😆&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;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;nextUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/members&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionClaims&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicMetadata&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;payment&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;paid&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;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Welcome paid member! 👋&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;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;nextUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/members&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
            &lt;span class="c1"&gt;// How we get payment value "paid" is next, in the webhook section!&lt;/span&gt;
      &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionClaims&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicMetadata&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;payment&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;paid&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;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// If we add more public routes, signed-in people can access them&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;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;nextUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/members&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;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Fallthrough last-ditch to allow access to a public route explicitly&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;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isPublicRoute&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="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&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;We can simplify the Middleware access logic for this app, but this explicit example can show how you can have far more complex access handling. Where do we get &lt;code&gt;“paid”&lt;/code&gt; from!? That is coming up next. &lt;/p&gt;

&lt;h3&gt;
  
  
  Webhook Endpoint
&lt;/h3&gt;

&lt;p&gt;Since this app is our SaaS with member access, we need to provide a way for the user to pay and gain access. Let’s start with setting up the tokens for Clerk &amp;amp; instantiating Stripe.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Stripe&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;STRIPE_SECRET_KEY&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2023-10-16&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;webhookSecret&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;STRIPE_WEBHOOK_SECRET&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After we have the credentials, the rest of the code should look very similar to the Stripe default logic for webhooks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Handling Webhook Events&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&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;NextRequest&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="nx"&gt;req&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&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="s2"&gt;`Missing userId or request`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cause&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="p"&gt;});&lt;/span&gt;
    &lt;span class="c1"&gt;// Stripe sends this for us 🎉&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stripeSignature&lt;/span&gt; &lt;span class="o"&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;headers&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="s2"&gt;stripe-signature&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// If we don't get it, we can't do anything else! &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;stripeSignature&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&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="s2"&gt;stripeSignature is null&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webhooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;constructEvent&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nx"&gt;stripeSignature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;webhookSecret&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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="nx"&gt;error&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&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="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&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;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// If we dont have the event, we can't do anything again&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;event&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&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="s2"&gt;`event is undefined`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkout.session.completed&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;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&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;object&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;`Payment successful for session ID: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;session&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;default&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;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Unhandled event type: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&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="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;success&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;p&gt;So what is next!? We need a way to know when a Clerk User has 'paid.' Well, let's extract that switch statement and add the secret sauce. That'll make this all work when we are done!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Adding Clerk User Metadata to Event&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkout.session.completed&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;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&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;object&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;`Payment successful for session ID: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;session&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="c1"&gt;// That's it? Yep, that's it. We love UX 🎉&lt;/span&gt;
            &lt;span class="nx"&gt;clerkClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateUserMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&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;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;publicMetadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="c1"&gt;// This is where we get "paid"&lt;/span&gt;
              &lt;span class="na"&gt;payment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payment_status&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="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;default&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;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Unhandled event type: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&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;Some of you may have noticed &lt;strong&gt;&lt;code&gt;event.data.object.metadata?.userId&lt;/code&gt;&lt;/strong&gt; (where did that come from!?). We will get to that one too. The reason for this is that we can’t access Clerk’s session in the webhook, so we will get a little creative.&lt;/p&gt;

&lt;h3&gt;
  
  
  Session Endpoint
&lt;/h3&gt;

&lt;p&gt;We will now need to create an endpoint that will generate our Stripe session that will be used to make our payment and turn our Clerk User into a paid &lt;code&gt;"Member"&lt;/code&gt;. This is where the &lt;code&gt;userId&lt;/code&gt; in the webhook will also be coming from! Instantiate &lt;code&gt;stripe&lt;/code&gt; the same as before, it will again be a Next.js &lt;code&gt;POST&lt;/code&gt; endpoint. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stripe Session&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This is our Clerk function for session User&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;userId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// We are receiving this from the Client request, thats next!&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;unit_amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;quantity&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="nx"&gt;req&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;try&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;session&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;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessions&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;payment_method_types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;card&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;line_items&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;price_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;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;usd&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;product_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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Membership&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;unit_amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="nx"&gt;quantity&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="c1"&gt;// This is where "event.data.object.metadata?.userId" is defined from! &lt;/span&gt;
      &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;payment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;success_url&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&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="s2"&gt;origin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;/members`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;cancel_url&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&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="s2"&gt;origin&lt;/span&gt;&lt;span class="dl"&gt;"&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&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="nx"&gt;session&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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;We have now laid the groundwork for a SaaS leveraging Clerk’s User Metadata to manage User specific data! So, to really focus on the versatility and potential of this feature, the UI portion has been kept really simple. We have the Homepage with a button to navigate to &lt;code&gt;/members&lt;/code&gt; page and to become a paid member, let’s take a look at the homepage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Homepage Implementation&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Home&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;isSignedIn&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAuth&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isSignedIn&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SignIn&lt;/span&gt; &lt;span class="na"&gt;redirectUrl&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&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="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;You are signed in!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CheckoutButton&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;
              &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;
              &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/members"&lt;/span&gt;
            &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              Go to members page
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrap up!
&lt;/h2&gt;

&lt;p&gt;This pattern can be used with any other transactions or user specific data you would like to handle in the backend and then utilize in the client. This keeps your User Management pragmatic &amp;amp; versatile, offloading the burden across multiple systems. This is only the beginning with what we can do with Clerk’s toolset, this time we only leveraged User Metadata! What should we do next? Let us know in the Discord and on X! &lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1527026488381296640-67" src="https://platform.twitter.com/embed/Tweet.html?id=1527026488381296640"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1527026488381296640-67');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1527026488381296640&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;Not forgetting, you will want the &lt;a href="https://github.com/JacobMGEvans/stripe-with-webhooks-and-metadata" rel="noopener noreferrer"&gt;complete codebase&lt;/a&gt; to check out, and learn from! &lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;List of resources, documentation, and links mentioned in the blog:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clerk Metadata: &lt;a href="https://clerk.com/docs/users/metadata?utm_source=www.google.com&amp;amp;utm_medium=referral&amp;amp;utm_campaign=none" rel="noopener noreferrer"&gt;Clerk Metadata Documentation&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>stripe</category>
      <category>learning</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>I'm Back! Burnout Sucks.</title>
      <dc:creator>Jacob Evans</dc:creator>
      <pubDate>Thu, 26 Oct 2023 15:34:25 +0000</pubDate>
      <link>https://dev.to/jacobmgevans/im-back-burnout-sucks-4p82</link>
      <guid>https://dev.to/jacobmgevans/im-back-burnout-sucks-4p82</guid>
      <description>&lt;h2&gt;
  
  
  Why I Took a Break and Why I'm Back!
&lt;/h2&gt;

&lt;p&gt;I stopped writing blogs, and shorts (like this one), other passion projects fell off too awhile back. Mainly I was focused on work and personal life, and was burnt out trying to build out my side projects at the same time. &lt;/p&gt;

&lt;p&gt;Am I gonna start firing at 110% again trying to do all the things at once... No. That is how we end up with giant gaps in activity, the longer they are the harder it is to restart too. &lt;/p&gt;

&lt;h2&gt;
  
  
  So, what's changed?
&lt;/h2&gt;

&lt;p&gt;Well I got a new job &lt;a class="mentioned-user" href="https://dev.to/authentication"&gt;@authentication&lt;/a&gt; (Clerk) as Sr. Developer Experience Engineer (cool title I know), additionally, personal life has stabilized a bit in the last few years, though I plan on moving again shortly. &lt;/p&gt;

&lt;p&gt;What does this mean... probably some streaming, work related and personal stuff like Dungeons &amp;amp; Dragons, trying to revive the Discord (more, its been active lately), a little writing and continuing Code &amp;amp; Life my 3 days a week Twitter Space I have been doing for 2+ years now &lt;/p&gt;

&lt;p&gt;Ready to join the conversation? Check out our Discord! &lt;a href="https://discord.gg/JTDfwPa2mX" rel="noopener noreferrer"&gt;https://discord.gg/JTDfwPa2mX&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And hey, don’t forget to visit my website &lt;a href="https://osrg.t3.gg" rel="noopener noreferrer"&gt;https://osrg.t3.gg&lt;/a&gt; for more updates and insights.&lt;/p&gt;

&lt;p&gt;See you around!&lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>How I am bringing OSS contributing to Twitch, taking everyone with me and more...</title>
      <dc:creator>Jacob Evans</dc:creator>
      <pubDate>Sat, 09 Jul 2022 19:56:04 +0000</pubDate>
      <link>https://dev.to/jacobmgevans/i-am-bringing-oss-contributing-to-twitch-taking-everyone-with-me-and-more-12g0</link>
      <guid>https://dev.to/jacobmgevans/i-am-bringing-oss-contributing-to-twitch-taking-everyone-with-me-and-more-12g0</guid>
      <description>&lt;h2&gt;
  
  
  Origin Story
&lt;/h2&gt;

&lt;p&gt;A little over a year ago I started a community (&lt;a href="//osrg.t3.gg"&gt;OSRG&lt;/a&gt;) that I wanted to build into a new way of approaching Open Source Contributing, emphasizing on gamification, crowdsourcing &amp;amp; mob programming as some main pillars. Very quickly we realized the power of this when we knocked out a MONSTER of a repository in 30 days converting it from Enzyme to React Testing Library, not long after I burned out from many different things. &lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1350089186175553537-208" src="https://platform.twitter.com/embed/Tweet.html?id=1350089186175553537"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1350089186175553537-208');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1350089186175553537&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  What now?
&lt;/h2&gt;

&lt;p&gt;Well during that time something I realized was a platform &amp;amp; system needed to be built for all this! Great! We also needed a community! We have one! Cool we are chugging along, people starting wondering "Why gamification? It is so much effort, who cares!?" Valid questions, well now I have a &lt;a href="https://www.twitch.tv/jacobmgevans" rel="noopener noreferrer"&gt;Twitch channel&lt;/a&gt; where I am merging my Nerddoms!! The plans are to take the channel building on the community, introduce more people to my passion for games like Dungeons &amp;amp; Dragons, coding, Open Source Contributing and combine them in the channel. Ideas like having streams with panels of people mob programming with me live on Twitch w/ people helping in chat, interacting on Discord, and even building the platform together in the open that will change how people approach coding! &lt;/p&gt;

</description>
      <category>opensource</category>
      <category>programming</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Fresh New Profile Pic</title>
      <dc:creator>Jacob Evans</dc:creator>
      <pubDate>Thu, 10 Feb 2022 03:54:46 +0000</pubDate>
      <link>https://dev.to/jacobmgevans/fresh-new-profile-pic-9kh</link>
      <guid>https://dev.to/jacobmgevans/fresh-new-profile-pic-9kh</guid>
      <description>&lt;p&gt;Figured it was about time to change a profile pic I had been using for 4 years. I have changed it across many platforms already. Twitter, GitHub, Dev, etc... &lt;/p&gt;

&lt;p&gt;Looking forward to not changing it again for awhile lol &lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>Git Graft: A NPX Tool &amp; Git Hook in TypeScript &amp; Node</title>
      <dc:creator>Jacob Evans</dc:creator>
      <pubDate>Fri, 04 Jun 2021 02:38:20 +0000</pubDate>
      <link>https://dev.to/jacobmgevans/git-graft-a-npx-tool-git-hook-in-typescript-node-14am</link>
      <guid>https://dev.to/jacobmgevans/git-graft-a-npx-tool-git-hook-in-typescript-node-14am</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;I decided I was tired of a GitFlow process. Every time I had to write a commit I had to prepend the JIRA ticket to the commit message, "It's in the branch, why can't it just take it from the branch!?" I kept asking myself. Finally, I said, I will just make that happen.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Idea
&lt;/h1&gt;

&lt;p&gt;Enter in the concept of Git Hooks, more famously known through tooling like Husky (awesome tool btw), however, there is a Git Hook for just about any part of the process of Git, and they have examples (written in shell, I'll get more into that later) in the &lt;code&gt;.git/hooks&lt;/code&gt; directory. Ok so it's right there, I made a hook what is the big deal? I thought to myself "It took me forever to do this because of the overhead what if I removed that for others?" So I did. &lt;/p&gt;

&lt;h1&gt;
  
  
  The Tool
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://nodei.co/npm/git-graft/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiqamsq1f0wnot80vz32u.png" alt="NPM" width="384" height="56"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Technologies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TypeScript &lt;/li&gt;
&lt;li&gt;NPM/NPX &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://oclif.io/" rel="noopener noreferrer"&gt;Oclif&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/enquirer/enquirer#-getting-started" rel="noopener noreferrer"&gt;Enquirer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So it might come as a bit of a surprise but there are only 2 shell files in this whole CLI tool. They really just execute the JavaScript. Even crazier, I made the &lt;code&gt;commit-msg&lt;/code&gt; hook entirely JavaScript little trick that I will get into later.&lt;/p&gt;

&lt;p&gt;I will go over the CLI &amp;amp; Git Commit Message Hook in the following 2 more blog articles in the series. I want to keep this one short and more of a summary. &lt;/p&gt;

</description>
      <category>git</category>
      <category>githook</category>
      <category>typescript</category>
      <category>cli</category>
    </item>
    <item>
      <title>Labeled Breakout &amp; GOTO</title>
      <dc:creator>Jacob Evans</dc:creator>
      <pubDate>Wed, 08 Jul 2020 02:49:28 +0000</pubDate>
      <link>https://dev.to/jacobmgevans/labeled-breakout-goto-20am</link>
      <guid>https://dev.to/jacobmgevans/labeled-breakout-goto-20am</guid>
      <description>&lt;p&gt;So I saw a post on Twitter, talking about labeled breaks... Which led to people fervently recommending not to do such patterns. Somehow this brought up GOTO, now this is the thing, I have never used GOTO, I Googled it. What I read was likely the correlation to the heavy abuse of GOTO in multi-loop breaks since the example was nested for loops using labeled breaks. &lt;/p&gt;

&lt;p&gt;From what I can tell, which is limited. The GOTO was essentially abstracted away by such things as &lt;code&gt;for&lt;/code&gt;, &lt;code&gt;while&lt;/code&gt;, &lt;code&gt;switch&lt;/code&gt;, &lt;code&gt;do&lt;/code&gt;, and the like. Reintroducing the GOTO paradigm seems redundant in structured programming. &lt;/p&gt;

&lt;p&gt;My naive thought about this is in a modern high-level language there is probably a builtin model that is optimized that does something similar. However, I brought it up here because I want to learn more about it and GOTO beyond some old code and Wikipedia. &lt;/p&gt;

</description>
      <category>healthydebate</category>
      <category>watercooler</category>
      <category>twitterverse</category>
    </item>
    <item>
      <title>Digital Garden</title>
      <dc:creator>Jacob Evans</dc:creator>
      <pubDate>Thu, 02 Jul 2020 23:34:28 +0000</pubDate>
      <link>https://dev.to/jacobmgevans/digital-garden-417c</link>
      <guid>https://dev.to/jacobmgevans/digital-garden-417c</guid>
      <description>&lt;p&gt;I eventually plan on building my personal website or portfolio... whatever you want to call it. I haven't because I have never felt the need too. How often has someone asked me about something I made? Too few. how often did they actually look at it after I pointed them towards one of those things? Nearly nil. &lt;/p&gt;

&lt;p&gt;So why would I care about the personal website now? Great question, it isn't for the usual reasons. I recently learned about this concept of Digital Gardens at first I thought, "This is the same thing I already am doing..." Blogging (on Dev.to), Twitter, GitHub, etc... all feeding into the zeitgeist that is &lt;strong&gt;ME&lt;/strong&gt; in tech.&lt;/p&gt;

&lt;p&gt;Well, not so much, after looking at &lt;a href="https://joelhooks.com/digital-garden" rel="noopener noreferrer"&gt;Joel Hooks Digital Garden&lt;/a&gt; it dawned on me, the potential for aggregating my thoughts, my ideas, my brainstorming and so much more in a completely abstract yet connected way is POWERFUL. &lt;/p&gt;

&lt;p&gt;Does this mean no more Dev.to for me? Well no, I love this platform. Does it mean I will be blasting this platform with my nonsense and stream of consciousness on Dev.to? Definitely not, I would spare you all from that misery. I do plan on connecting some of what I do in my Digital Garden to this platform and others I listed, but you can think of what I bring from the Digital Garden to here as the &lt;strong&gt;harvested&lt;/strong&gt; crop, the fully formulated ideas, and thoughts growing there.&lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
  </channel>
</rss>
