<?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: Joshua Nussbaum</title>
    <description>The latest articles on DEV Community by Joshua Nussbaum (@joshnuss).</description>
    <link>https://dev.to/joshnuss</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%2F142518%2F9201d23b-fef6-4df6-b9a4-141911a637ee.png</url>
      <title>DEV Community: Joshua Nussbaum</title>
      <link>https://dev.to/joshnuss</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/joshnuss"/>
    <language>en</language>
    <item>
      <title>Gating entrances with Stripe and NFC passes</title>
      <dc:creator>Joshua Nussbaum</dc:creator>
      <pubDate>Thu, 21 Aug 2025 10:24:05 +0000</pubDate>
      <link>https://dev.to/joshnuss/gating-entrances-with-stripe-and-nfc-passes-43na</link>
      <guid>https://dev.to/joshnuss/gating-entrances-with-stripe-and-nfc-passes-43na</guid>
      <description>&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ℹ️&lt;/th&gt;
&lt;th&gt;Re-posted from &lt;a href="https://stripe.dev/blog/gating-entrances-with-stripe-and-nfc-passes" rel="noopener noreferrer"&gt;stripe.dev&lt;/a&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;While working remotely and traveling from place to place, I noticed a recurring pattern.&lt;/p&gt;

&lt;p&gt;Every time you arrive in a new city, you rent an Airbnb or check-in to a hotel. Then you sign up for a gym and a co-working space.&lt;/p&gt;

&lt;p&gt;Payments for these services are all done electronically. But when it comes to access, you need a plastic card, a fob, or a physical key.&lt;/p&gt;

&lt;p&gt;This creates friction for everyone involved. You have to go pick it up, carry it around, hope you don’t lose it, and return it before leaving.&lt;/p&gt;

&lt;p&gt;From the business side, they need to pay someone to work the front desk to issue, track, and collect these access devices.&lt;/p&gt;

&lt;p&gt;This made me wonder, what if access was fully digital?&lt;/p&gt;

&lt;h2&gt;
  
  
  Digital wallet passes
&lt;/h2&gt;

&lt;p&gt;Luckily, there is a better way that is gaining popularity.&lt;/p&gt;

&lt;p&gt;Instead of cards and keys, we can issue digital passes with Google Wallet and Apple Wallet and use an NFC reader to gate the entrance.&lt;/p&gt;

&lt;p&gt;These digital passes are like certificates that are scannable with NFC. They can be issued and revoked electronically without human intervention (no front desk needed).&lt;/p&gt;

&lt;p&gt;Since everyone already has a smart phone or watch with Apple Wallet &amp;amp; Google Wallet, there’s nothing extra to install or carry.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integration
&lt;/h3&gt;

&lt;p&gt;Both Apple and Google have public Wallet APIs for issuing passes, but for smaller companies it's easier to use an intermediary like &lt;a href="https://www.passninja.com/" rel="noopener noreferrer"&gt;PassNinja&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.passninja.com/" rel="noopener noreferrer"&gt;PassNinja&lt;/a&gt; acts as an abstraction layer on top of Apple &amp;amp; Google, so you only have to integrate one API.&lt;/p&gt;

&lt;p&gt;Then you can place an Apple/Google compatible NFC reader near the doorway. We'll use the &lt;a href="https://www.dotorigin.com" rel="noopener noreferrer"&gt;DotOrigin&lt;/a&gt; VTAP100.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building it
&lt;/h2&gt;

&lt;p&gt;As an example, we'll build a simple gym membership system.&lt;br&gt;
&lt;a href="https://github.com/blackline-commerce/stripe-nfc-gate" rel="noopener noreferrer"&gt;Full Source Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Members can purchase a Stripe Subscription and a digital pass will be issued to them. The member can then use their phone (or watch) to access the doorway.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwo75nvg0y5u6n2r0rebz.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%2Fwo75nvg0y5u6n2r0rebz.png" alt="Application flow" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There will be two codebases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Website&lt;/strong&gt;: A public facing website that accepts payments via Stripe Checkout and issues the digital passes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gate&lt;/strong&gt;: A private access control system that runs on Linux near the entrance. It will verify the status of the membership by contacting the Stripe Subscription API, and unlock the door for active members.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Architecture
&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%2F5swegmmh2n7agjdrbpu9.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%2F5swegmmh2n7agjdrbpu9.png" alt="Architecture diagram" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Hardware
&lt;/h2&gt;

&lt;p&gt;Here's what you'll need to build the solution:&lt;/p&gt;
&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Computer&lt;/strong&gt;: A &lt;a href="https://www.raspberrypi.com/products/raspberry-pi-zero-2-w/" rel="noopener noreferrer"&gt;Raspberry PI Zero 2W&lt;/a&gt; or equivalent. For communicating between NFC Reader and Stripe API over WiFi.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NFC Reader&lt;/strong&gt;: &lt;a href="https://shop.vtapnfc.com/product/vtap100-embedded-nfc-reader-board" rel="noopener noreferrer"&gt;VTAP100&lt;/a&gt;, an Apple / Google Wallet compatible NFC reader.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Electronic Door Strike/Bolt&lt;/strong&gt;: To unlock and lock the door.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relay&lt;/strong&gt;: A &lt;a href="https://abra-electronics.com/electromechanical/relays/relay-modules-shields/rm-1-3-3v-t-single-isolated-3-3v-relay-high-low-trigger-module-10a.html" rel="noopener noreferrer"&gt;3.3V Relay&lt;/a&gt; or equivalent. To control locking and unlocking the door strike from the Raspberry PI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PassNinja Account&lt;/strong&gt;: To handle issuing of Google &amp;amp; Apple Wallet Passes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Wiring diagram
&lt;/h3&gt;

&lt;p&gt;The wiring will look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2iy8swui3hpqzjdz6bua.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%2F2iy8swui3hpqzjdz6bua.png" alt="Wiring diagram" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  PassNinja setup
&lt;/h2&gt;

&lt;p&gt;To set up PassNinja:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create an account.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Visit &lt;a href="https://www.passninja.com/" rel="noopener noreferrer"&gt;https://www.passninja.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; Click "Get Started"&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a pass template&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Log in to PassNinja: &lt;a href="https://www.passninja.com/login" rel="noopener noreferrer"&gt;https://www.passninja.com/login&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; Click on "Dashboard" in the upper right corner&lt;/li&gt;
&lt;li&gt; Click on "New Pass Template" button and create a template for Google &amp;amp; Apple&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Setup the NFC Reader:&lt;br&gt;
Follow these instructions &lt;a href="https://www.passninja.com/tutorials/hardware/how-to-configure-a-dot-origin-vtap100-nfc-reader" rel="noopener noreferrer"&gt;https://www.passninja.com/tutorials/hardware/how-to-configure-a-dot-origin-vtap100-nfc-reader&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Purchase flow
&lt;/h3&gt;

&lt;p&gt;The purchase part is on a public website that handles the checkout and issuing of passes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc3av994758mgkl9se83q.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%2Fc3av994758mgkl9se83q.png" alt="Purchase flow sequence diagram" width="800" height="565"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’ll have an endpoint /subscribe to create the Stripe Checkout session and redirect the user to pay:&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="c1"&gt;// in web/src/index.ts&lt;/span&gt;

&lt;span class="c1"&gt;// create a Stripe API client&lt;/span&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;your stripe key&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// when user visits /subscribe, a Stripe checkout session is created and they're redirected to pay&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/subscribe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&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;// this is where the user is redirected after payment`  &lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;success_url&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;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/success?session_id={CHECKOUT_SESSION_ID}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;your domain&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="c1"&gt;// create a checkout session`  &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;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;subscription&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;success_url&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&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;your price id&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;quantity&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="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="c1"&gt;// redirect the user to pay&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;c&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="nx"&gt;session&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the user finishes paying, Stripe Checkout will redirect them to /success, and the digital pass can then be issued:&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="c1"&gt;// in web/src/index.ts&lt;/span&gt;

&lt;span class="c1"&gt;// create the Pass Ninja API client&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;passNinja&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;PassNinjaClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;account id&amp;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="s1"&gt;&amp;lt;api key&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// when user visits /success (after checkout completes), issue the digital pass &lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&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;// get checkout session_id`  &lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;c&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="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;session_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// get the Stripe checkout session&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;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// get the Stripe subscription&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subscription&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;subscriptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieve&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;subscription&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// ensure the subscription status is active`  &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;subscription&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Subscription was not successful&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// we've confirmed it's paid, so issue a new pass`  &lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pass&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;passNinja&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pass&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="nx"&gt;PASSNINJA_PASS_TYPE&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="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customer_details&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customer_details&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

       &lt;span class="c1"&gt;// *important*: save the subscription_id inside the pass&lt;/span&gt;
      &lt;span class="c1"&gt;// this is the value the NFC reader sends during a scan&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nfc-message&lt;/span&gt;&lt;span class="dl"&gt;"&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;subscription&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;  
  &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// redirect the user to add the pass to their wallet&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;c&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="nx"&gt;pass&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Door access control
&lt;/h3&gt;

&lt;p&gt;The door access logic can run on any computer, but we'll use a Raspberry PI Zero 2W which is an inexpensive option (~$15 USD) and has the ability to control a relay.&lt;/p&gt;

&lt;p&gt;The sequence looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ecrt8emv3uzied9tz4p.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%2F4ecrt8emv3uzied9tz4p.png" alt="Door access sequence diagram" width="800" height="565"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The NFC reader acts as a virtual serial port, and each time a user’s phone or watch is placed near it, a new line is sent over the serial port.&lt;/p&gt;

&lt;p&gt;Linux typically maps virtual serial ports to &lt;code&gt;/dev/ttyACM0&lt;/code&gt;. To access it from Node.js, we'll use the npm package &lt;code&gt;serialport&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in gate/src/index.ts`  &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;SerialPort&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="nx"&gt;serialport&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;&lt;span class="s2"&gt;`

// create a serial port client`&lt;/span&gt;  
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&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;SerialPort&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/dev/ttyACM0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;baudRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;9600&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="c1"&gt;// use ReadlineParser, so that we receive a full lines&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&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;ReadlineParser&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;delimiter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt;

&lt;span class="c1"&gt;// a new line is sent whenever a pass is near the NFC reader&lt;/span&gt;
&lt;span class="nx"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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;// verify pass here&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The data sent comes from the nfc-message field of the pass, which in our case is the Stripe Subscription ID (starts with &lt;code&gt;sub_&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;We can use that ID to verify that the subscription status is &lt;code&gt;active&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in gate/src/index.ts&lt;/span&gt;

&lt;span class="nx"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subscription_id&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;// retrieve the subscription record`  &lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subscription&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;subscriptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subscription_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// check if subscription is active`  &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;subscription&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&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="c1"&gt;// flash LEDs green, play sound, and trigger relay to unlock the door&lt;/span&gt;
    &lt;span class="nf"&gt;success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="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;`Access allowed. id=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;subscription_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="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// flash LEDs red and play sound  &lt;/span&gt;
    &lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Access denied. id=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;subscription_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, status=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;subscription&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The success logic will then open the door bolt by triggering the relay:&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="c1"&gt;// in gate/src/index.ts&lt;/span&gt;

&lt;span class="c1"&gt;// setup a connection to the relay on GPIO #8&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;relay&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;Gpio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// open the door bolt for 5 seconds`  &lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// turn relay on to unlock the door bolt&lt;/span&gt;
  &lt;span class="nx"&gt;relay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;high&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="c1"&gt;// in 5 seconds, turn relay off. &lt;/span&gt;
  &lt;span class="c1"&gt;// this causes the door bolt to lock. &lt;/span&gt;
  &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;relay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;low&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="nx"&gt;_000&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;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Using digital passes makes physical gating much simpler. &lt;/p&gt;

&lt;p&gt;It allows merchants to sell and grant access completely digitally, and users don't have to deal with picking up, carrying, replacing, sharing and returning cards and keys.&lt;/p&gt;

&lt;p&gt;It’s as easy as issuing a pass after payment, and adding an NFC reader to the entrance to verify the status of payment.&lt;/p&gt;

&lt;p&gt;Special thanks to &lt;a href="https://www.linkedin.com/in/corumba" rel="noopener noreferrer"&gt;Bill Scott&lt;/a&gt; at &lt;a href="https://www.dotorigin.com/" rel="noopener noreferrer"&gt;DotOrigin&lt;/a&gt; for sharing his knowledge on this topic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/blackline-commerce/stripe-nfc-gate" rel="noopener noreferrer"&gt;Source Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stripe.com" rel="noopener noreferrer"&gt;Stripe&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.dotorigin.com/" rel="noopener noreferrer"&gt;DotOrigin/VTAP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.passninja.com/" rel="noopener noreferrer"&gt;PassNinja&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.raspberrypi.com/products/raspberry-pi-zero-2-w/" rel="noopener noreferrer"&gt;Raspberry PI Zero 2W&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>stripe</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>raspberrypi</category>
    </item>
    <item>
      <title>Rust on a $2 dev board</title>
      <dc:creator>Joshua Nussbaum</dc:creator>
      <pubDate>Fri, 13 Dec 2024 03:00:21 +0000</pubDate>
      <link>https://dev.to/joshnuss/rust-on-a-5-dev-board-2335</link>
      <guid>https://dev.to/joshnuss/rust-on-a-5-dev-board-2335</guid>
      <description>&lt;p&gt;Rust is a great option for embedded computing, especially for small devices like ESP32.&lt;/p&gt;

&lt;p&gt;There is a very inexpensive dev board, called ESP32-C3 Super Mini which sells for ~$2 on AliExpress.&lt;/p&gt;

&lt;p&gt;The chip is RISC-based, supports Wifi &amp;amp; Bluetooth, and includes a built-in LED - which we'll use in this project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Rust
&lt;/h2&gt;

&lt;p&gt;First you'll need Rust on your machine.&lt;/p&gt;

&lt;p&gt;The toolchain can be installed via &lt;a href="https://www.rust-lang.org/tools/install" rel="noopener noreferrer"&gt;Rustup&lt;/a&gt;, or (my preferred way) using &lt;a href="https://asdf-vm.com" rel="noopener noreferrer"&gt;&lt;code&gt;asdf&lt;/code&gt;&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt;: &lt;code&gt;asdf&lt;/code&gt; is a great way to manage multiple versions of programming language tools.&lt;/p&gt;

&lt;p&gt;To install with &lt;code&gt;asdf&lt;/code&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;# add rust plugin&lt;/span&gt;
asdf plugin-add rust https://github.com/asdf-community/asdf-rust.git

&lt;span class="c"&gt;# install rust&lt;/span&gt;
asdf &lt;span class="nb"&gt;install &lt;/span&gt;rust 1.81.0

&lt;span class="c"&gt;# set default version&lt;/span&gt;
asdf global rust 1.81.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create the project
&lt;/h2&gt;

&lt;p&gt;Let's create the project folder structure.&lt;/p&gt;

&lt;p&gt;We can start with the template:&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="nv"&gt;$&amp;gt;&lt;/span&gt; cargo generate esp-rs/esp-idf-template cargo

⚠️   Favorite &lt;span class="sb"&gt;`&lt;/span&gt;esp-rs/esp-idf-template&lt;span class="sb"&gt;`&lt;/span&gt; not found &lt;span class="k"&gt;in &lt;/span&gt;config, using it as a git repository: https://github.com/esp-rs/esp-idf-template.git
🤷   Project Name: blink
🔧   Destination: some/path/blink ...
🔧   project-name: blink ...
🔧   Generating template ...
✔ 🤷   Which MCU to target? · esp32c3
✔ 🤷   Configure advanced template options? · &lt;span class="nb"&gt;false&lt;/span&gt;
🔧   Moving generated files into: &lt;span class="sb"&gt;`&lt;/span&gt;some/path/blink&lt;span class="sb"&gt;`&lt;/span&gt;...
🔧   Initializing a fresh Git repository
✨   Done! New project created some/path/blink

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll need access to the HAL (hardware abstraction layer), so install the &lt;code&gt;esp-idf-hal&lt;/code&gt; crate too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd blink
cargo add esp-idf-hal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Code
&lt;/h2&gt;

&lt;p&gt;Now add this to &lt;code&gt;src/main.rs&lt;/code&gt; to blink the LED:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;esp_idf_hal&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;FreeRtos&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;esp_idf_hal&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;gpio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;esp_idf_hal&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peripherals&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Peripherals&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// enable logging&lt;/span&gt;
    &lt;span class="nn"&gt;esp_idf_svc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;EspLogger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;initialize_default&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// get peripherals&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peripherals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Peripherals&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// built-in LED is GPIO8&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;led&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;PinDriver&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peripherals&lt;/span&gt;&lt;span class="py"&gt;.pins.gpio8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// alternate between high and low, with delay of 1s&lt;/span&gt;
    &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"high"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;led&lt;/span&gt;&lt;span class="nf"&gt;.set_high&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nn"&gt;FreeRtos&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;delay_ms&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"low"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;led&lt;/span&gt;&lt;span class="nf"&gt;.set_low&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nn"&gt;FreeRtos&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;delay_ms&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&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;
  
  
  Running the code
&lt;/h2&gt;

&lt;p&gt;Connect the dev board to USB, and then flash it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The LED should now be blinking, and the message "HIGH"/"LOW" will be displayed in the terminal.&lt;/p&gt;

&lt;p&gt;Happy hacking ✌️&lt;/p&gt;

&lt;p&gt;P.S. Here's the source code:&lt;br&gt;
&lt;a href="https://github.com/joshnuss/esp-blink-rs" rel="noopener noreferrer"&gt;https://github.com/joshnuss/esp-blink-rs&lt;/a&gt;&lt;/p&gt;

</description>
      <category>esp32</category>
      <category>rust</category>
      <category>hardware</category>
    </item>
    <item>
      <title>Math concepts explained with JavaScript</title>
      <dc:creator>Joshua Nussbaum</dc:creator>
      <pubDate>Wed, 18 Sep 2024 07:20:43 +0000</pubDate>
      <link>https://dev.to/joshnuss/using-code-to-understand-math-concepts-41bf</link>
      <guid>https://dev.to/joshnuss/using-code-to-understand-math-concepts-41bf</guid>
      <description>&lt;p&gt;Learning mathematical notation can feel intimidating.&lt;/p&gt;

&lt;p&gt;So I created list of JS code examples for common math symbols:&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcpe6vo9p7e305bc3i6sv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcpe6vo9p7e305bc3i6sv.png" alt="Screenshot of Math4Devs.com" width="800" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PRs welcome!&lt;br&gt;
&lt;a href="https://github.com/joshnuss/math4devs.com" rel="noopener noreferrer"&gt;https://github.com/joshnuss/math4devs.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>javascript</category>
      <category>math</category>
    </item>
    <item>
      <title>Is rewriting software bad?</title>
      <dc:creator>Joshua Nussbaum</dc:creator>
      <pubDate>Tue, 20 Aug 2024 16:01:01 +0000</pubDate>
      <link>https://dev.to/joshnuss/is-rewriting-software-bad-5229</link>
      <guid>https://dev.to/joshnuss/is-rewriting-software-bad-5229</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;"The universe likes nothing more than to transform that which was, into that which will be." - Marcus Aurelius&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;How did our code get so complicated?&lt;/p&gt;

&lt;p&gt;Over time, many &lt;strong&gt;small&lt;/strong&gt; well-intentioned changes &lt;strong&gt;pile up&lt;/strong&gt;, they compound, and eventually bring on &lt;strong&gt;accidental complexity&lt;/strong&gt;. Stuff that was once easy to do, becomes harder. The complexity tax kicks in and everything starts feeling like a heavy lift.&lt;/p&gt;

&lt;p&gt;So, what can we do?&lt;/p&gt;

&lt;p&gt;We've all heard that rewriting software is &lt;strong&gt;dangerous&lt;/strong&gt;, that it &lt;strong&gt;wastes&lt;/strong&gt; valuable time. "If it ain't broke don't fix it", right?&lt;/p&gt;

&lt;p&gt;I'm here to tell you, I've rewritten software, and it made everything much better and easier to work with.&lt;/p&gt;

&lt;p&gt;When done properly, &lt;strong&gt;a rewrite can free up a lot of energy that was lost to dealing with the complexity&lt;/strong&gt;. It removes the complexity tax and makes the business's goals cheaper.&lt;/p&gt;

&lt;p&gt;Sure, it's no silver bullet - nothing is - but over the course of building a system, we learn new things. These lessons need to be factored back in to the code, &lt;strong&gt;not just in incremental steps&lt;/strong&gt;, but also by making their way &lt;strong&gt;back into the design&lt;/strong&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  To change? or to stay the same?
&lt;/h1&gt;

&lt;p&gt;Basecamp was re-written 3 times. Yankee Stadium was rebuilt in 2008.&lt;br&gt;
But many banks still run COBOL systems written in the 1960's.&lt;/p&gt;

&lt;p&gt;So, who is right?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There is no hard and fast rule&lt;/strong&gt;. Every situation is different. Everyone should analyze their specific situation and decide for themselves. Refactoring is enough in most cases.&lt;/p&gt;

&lt;p&gt;And re-writing isn't a binary thing either. It's totally valid to re-write one part and refactor another. It's just another tool to have in the tool belt.&lt;/p&gt;

&lt;h1&gt;
  
  
  Re-write indicators
&lt;/h1&gt;

&lt;p&gt;Here are some indicators that a rewrite is worth considering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Have easy things become difficult?&lt;/strong&gt;&lt;br&gt;
There could be incorrect or missing abstractions. The abstractions may have once been correct, but wore down and no longer fit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Are upcoming changes costly?&lt;/strong&gt;&lt;br&gt;
If new features are on the horizon and they are expected to be difficult, it could be that a re-write will make that easier. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;When making changes, do they cause unexpected impact in unrelated places?&lt;/strong&gt;&lt;br&gt;
Dependencies between parts may not be clearly specified.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Is production tracing relied on heavily to verify how things works?&lt;/strong&gt;&lt;br&gt;
The complexity may have gotten so out of hand, devs are having a hard time understanding it. So they rely on logs to understand how the system works. Re-writing can bring back clarity to the design.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Can some changes only be accomplished by specific people?&lt;/strong&gt;&lt;br&gt;
There could be tribal knowledge or optimizations that are non-obvious. Re-writing can re-organize the abstractions and make them clearer, removing the need for tribal knowledge.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Is more time spent on feature development or maintenance?&lt;/strong&gt;&lt;br&gt;
Constant maintenance can be thought of a design defect. It's a good idea to build features into the system to reduce maintenance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Do upgrades break things that used to work?&lt;/strong&gt;&lt;br&gt;
The system might need better verifiability. It could be missing automated tests, monitoring, or a paced rollout.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Refactor vs. Re-write
&lt;/h1&gt;

&lt;p&gt;When should you use one over the other?&lt;/p&gt;

&lt;p&gt;Both are methods of improving code. They are just on different sides of the "improvement scale".&lt;/p&gt;

&lt;p&gt;Think of refactoring like &lt;strong&gt;renovating&lt;/strong&gt; a house. Rewriting is like &lt;strong&gt;rebuilding&lt;/strong&gt; the house.&lt;/p&gt;

&lt;p&gt;Always prefer small renovations where possible, they're cheaper and less risky. But many renovation jobs might be more expensive than a rebuild.&lt;/p&gt;

&lt;p&gt;For example, renovating a 2-bedroom home into an 8-unit residential building wouldn't make sense, because it needs a different type of foundation.&lt;/p&gt;

&lt;p&gt;Ironically, it's usually the more important and successful stuff that is worth rebuilding, like the New York Yankees rebuilding Yankee Stadium in 2008. It's not that the old Yankee Stadium wasn't selling out - it was quite successful - that's what made it worth rebuilding. &lt;/p&gt;

&lt;h1&gt;
  
  
  Common arguments against re-writes
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;If it ain't broke, don't fix it.&lt;/strong&gt;&lt;br&gt;
Don't forget to consider the time spent on it. That may indicate that it is breaking from a cost perspective.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Won't re-writing waste valuable time?&lt;/strong&gt;&lt;br&gt;
Consider how much time is currently spent by not improving it?&lt;br&gt;
Or, how much time could be saved if it was improved?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why spend time on something that doesn't improve the business?&lt;/strong&gt;&lt;br&gt;
The goal of re-writing &lt;em&gt;is&lt;/em&gt; to improve the business. By re-writing we are protecting a valuable asset, reducing labor, and freeing devs to work on other impactful things for customers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After this re-write, what stops you from wanting another?&lt;/strong&gt;&lt;br&gt;
If a system warrants change then please re-write it again. As many times as it is valuable. Rewrite is just a synonym for improvement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customers don't care about code quality, they just want features.&lt;/strong&gt;&lt;br&gt;
The state of the code &lt;em&gt;is&lt;/em&gt; the ability to ship new features. Customers may not know about code quality directly, but indirectly it impacts them a lot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Our customers want a stable system.&lt;/strong&gt;&lt;br&gt;
Re-writing can be done while maintaining stability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This system took years to build, won't it take years to rewrite?&lt;/strong&gt;&lt;br&gt;
It shouldn't, when done correctly. Rewriting is about compacting existing code and decisions into something simpler. It doesn't mean re-visiting past decisions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The people that wrote this aren't here anymore, won't their knowledge be lost?&lt;/strong&gt;&lt;br&gt;
If their knowledge isn't codified in the system, in its documentation, or in its tests, then technically it's already been lost.&lt;/p&gt;

&lt;h1&gt;
  
  
  When to avoid the rewrite
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;When refactoring will do the job.&lt;/li&gt;
&lt;li&gt;When the system is humming along without any problems.&lt;/li&gt;
&lt;li&gt;When the people doing the rewrite aren't experienced yet. It's best to avoid rewriting a system before deeply understanding how it works. See: &lt;a href="https://en.wikipedia.org/wiki/Wikipedia:Chesterton%27s_fence" rel="noopener noreferrer"&gt;Chesterton's Fence&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;When it's just to use a different stack.&lt;/li&gt;
&lt;li&gt;When the system isn't important. It's probably not worth investing the energy. Better to find something else that is important and improve that.&lt;/li&gt;
&lt;li&gt;When there isn't a culture that values continuous improvement. If so, cultivate that first.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;Ironically, when we try to stabilize a code-base by avoiding change, we increase the cost of changes, fear of change sets in, and it can end up hurting the resiliency of the system.&lt;/p&gt;

&lt;p&gt;Incremental change is great, but it's not enough. The lessons we've learned over time need to make their way back into the design.&lt;/p&gt;

&lt;p&gt;Re-writing is a great way for systems to remain relevant and keep costs down.&lt;/p&gt;

&lt;p&gt;Don't believe the negative press it gets, it can save companies a lot of money.&lt;/p&gt;

</description>
      <category>management</category>
      <category>softwareengineering</category>
      <category>webdev</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Using experiments to ship faster</title>
      <dc:creator>Joshua Nussbaum</dc:creator>
      <pubDate>Tue, 02 Jul 2024 16:47:00 +0000</pubDate>
      <link>https://dev.to/joshnuss/using-experiments-to-ship-faster-5329</link>
      <guid>https://dev.to/joshnuss/using-experiments-to-ship-faster-5329</guid>
      <description>&lt;p&gt;Has this ever happened to you?&lt;/p&gt;

&lt;p&gt;You're eager to start coding, so you open your editor and start smashing the keyboard. After a few weeks something unexpected happens, you encounter a major blocker.&lt;/p&gt;

&lt;p&gt;Now you have to rework the codebase to get around the problem and a lot of high quality work gets scrapped.&lt;/p&gt;

&lt;p&gt;This is very expensive and wasteful and happens all too frequently. But there is a way to reduce the chance this happens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoiding "Fake progress"
&lt;/h2&gt;

&lt;p&gt;When a project starts, there can be pressure to show fast progress.&lt;/p&gt;

&lt;p&gt;But unfortunately, not all progress is equal.&lt;/p&gt;

&lt;p&gt;Quick progress might make your stakeholders happy early on. &lt;/p&gt;

&lt;p&gt;But at what expense?&lt;/p&gt;

&lt;p&gt;If it's at the expense of completing the project, or just delaying the harder parts for later, then it can be considered "fake progress".&lt;/p&gt;

&lt;p&gt;If we start out by building the obvious parts we already know how to do - are we really making meaningful progress? or just delaying the uncertainty?&lt;/p&gt;

&lt;p&gt;What if we could change that?&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding the Real work
&lt;/h2&gt;

&lt;p&gt;Meaningful progress comes from solving difficult issues as early as possible. This is real work. It can reduce the cost of projects by orders of magnitude.&lt;/p&gt;

&lt;p&gt;Because the faster we can locate real problems, and deal with them, the more meaningful headway we'll make.&lt;/p&gt;

&lt;p&gt;What we need is a way to locate the biggest unknowns, frictions, and blockers as early as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Improving work with experiments
&lt;/h2&gt;

&lt;p&gt;One solution is to make a list of the hard parts (aka "unknowns") and experiment with those before you start writing high quality code.&lt;/p&gt;

&lt;p&gt;These experiments are great, because with just a small amount of work they can remove large uncertainties from the project. They allow us to encounter uncertainties as early as possible, and eliminate them before they become costly blockers.&lt;/p&gt;

&lt;p&gt;Instead of starting a project by writing code in a complex code base and wasting energy on integration and quality. Experimenting allows us to test problems in isolation, without the cost of integration. This keeps re-work low, because we're not undoing complex production code written at a high quality bar.&lt;/p&gt;

&lt;p&gt;Once most of the unknowns are resolved, coding becomes a lot easier. It's just a matter of connecting the discovered solutions together, just like snapping together a bunch of LEGOs.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to experiment
&lt;/h2&gt;

&lt;p&gt;Next time you're starting a project, do this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Collect&lt;/strong&gt; a list of unknowns. These are things you think will be hard, or haven't done before.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prioritize&lt;/strong&gt; the list to tackle the most critical issues first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Estimate&lt;/strong&gt; how much each experiment will take. For example, 1-2 hours each. We don't want to spend forever on it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then for each experiment:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Build&lt;/strong&gt; a minimal solution, being mindful of the timebox.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capture&lt;/strong&gt; the result. Either in a GitHub repo or a code snippet on your machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Document&lt;/strong&gt; the solution or any caveats encountered.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once you've worked through your list, and assuming you haven't encountered any major blockers, you're ready to start building! yay&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding success
&lt;/h2&gt;

&lt;p&gt;To ensure your experiments are successful, here's some things to be mindful of:&lt;/p&gt;

&lt;h3&gt;
  
  
  Be careful of time management
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Don't overdo it.&lt;/strong&gt; For a 6 week project, a good number of experiments is 10-15. These should take 1-3 days to finish. It's important to timebox these.&lt;/p&gt;

&lt;h3&gt;
  
  
  Long experiments are bad experiments
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Keep your experiments small.&lt;/strong&gt; Break them into smaller chunks if needed. If they're taking too long, it could be a tell that your project is too big and that the scope should be reduced.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't experiment with obvious things
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;If you know how to do it, it's not unknown anymore.&lt;/strong&gt; For example, if you've built a login form before, no need to spend an experiment on that. Focus on stuff you don't know.&lt;/p&gt;

&lt;h3&gt;
  
  
  Be careful when re-using an experiment
&lt;/h3&gt;

&lt;p&gt;Sometimes it can helpful to build a series of experiments, where each one depends on the code of the last, but sometimes it's not. Remember to always consider what the integration cost is. &lt;strong&gt;A key point of experimenting is avoiding the cost of connecting parts together.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep the quality low
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Don't write clean code.&lt;/strong&gt; Don't write DRY or SOLID code. Don't write tests. The only thing we want is a working solution. It can be super ugly. Worry about quality later, when you start building.&lt;/p&gt;

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

&lt;p&gt;Reducing the uncertainty in a project, is more valuable than any TODO you can check off.&lt;/p&gt;

&lt;p&gt;And you'll end up shipping projects much faster too!&lt;/p&gt;

&lt;p&gt;Happy coding ✌️&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>WebAssembly: byte-code of the future</title>
      <dc:creator>Joshua Nussbaum</dc:creator>
      <pubDate>Mon, 14 Aug 2023 14:41:03 +0000</pubDate>
      <link>https://dev.to/joshnuss/webassembly-byte-code-of-the-future-402p</link>
      <guid>https://dev.to/joshnuss/webassembly-byte-code-of-the-future-402p</guid>
      <description>&lt;p&gt;Ever since Netscape launched JavaScript, there were some developers that liked it and others that didn't.&lt;/p&gt;

&lt;p&gt;Regardless of which side you're on, I think we can all agree that &lt;strong&gt;it would be good if browsers supported more programming languages&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is the promise of WebAssembly: Providing a &lt;strong&gt;generic runtime&lt;/strong&gt; that any programming language can compile to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Past Attempts
&lt;/h2&gt;

&lt;p&gt;In the earlier days of the web, extensions were attempted with Java Applets and Microsoft ActiveX. But both were plagued by security issues and eventually dropped. The problem was they executed without access controls, which became a massive attack surface.&lt;/p&gt;

&lt;p&gt;Later, Macromedia Flash and Silverlight had some success, but eventually met the same tragic fate. They both lacked an open standard, which made it hard for browser and OS vendors to support.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;WebAssembly&lt;/strong&gt; (aka WASM) is an &lt;a href="https://www.w3.org/groups/wg/wasm"&gt;open standard&lt;/a&gt; byte code format that works in all browsers. It's a low-level binary format and execution engine, conceptually similar to Oracle's JVM or Microsoft's CLR.&lt;/p&gt;

&lt;p&gt;It's designed from the ground up to be hosted and safe. It &lt;strong&gt;cannot&lt;/strong&gt; access the machine's memory or hard drive. Only the host can decide what APIs to expose.&lt;/p&gt;

&lt;p&gt;WASM is a portable format, so it can support many programming languages. Think Rust, Ruby, Python and even JavaScript can be compiled to WASM byte code.&lt;/p&gt;

&lt;p&gt;Though it was originally designed to target the browser, it works well outside the browser too.&lt;/p&gt;

&lt;p&gt;It can run on the server, in the cloud, in hardware devices, or used a plugin system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing WASM
&lt;/h2&gt;

&lt;p&gt;There are several ways to create a &lt;code&gt;.wasm&lt;/code&gt; file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write it by hand. (not recommended)&lt;/li&gt;
&lt;li&gt;Write it with &lt;a href="https://webassembly.github.io/spec/core/text/index.html"&gt;Wasm Text Format&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Use a higher level language like AssemblyScript, Rust, Ruby, etc.. and then compile it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'll show you a few examples:&lt;/p&gt;

&lt;h3&gt;
  
  
  Wat is WAT?
&lt;/h3&gt;

&lt;p&gt;The WASM specification provides a text-based format for defining WASM modules that is called WAT (WAsm Text format). It uses S-expresions, similar to Lisp or Clojure.&lt;/p&gt;

&lt;p&gt;Here's what a basic module looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="c1"&gt;; define a module&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;module&lt;/span&gt;
  &lt;span class="c1"&gt;; define a function called "add"&lt;/span&gt;
  &lt;span class="c1"&gt;; it takes 2 params:&lt;/span&gt;
  &lt;span class="c1"&gt;; - $a is a 32-bit integer&lt;/span&gt;
  &lt;span class="c1"&gt;; - $b is a 32-bit integer&lt;/span&gt;
  &lt;span class="c1"&gt;; it returns an 32-bit integer&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;fun&lt;/span&gt; &lt;span class="nv"&gt;add&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;param&lt;/span&gt; &lt;span class="nv"&gt;$a&lt;/span&gt; &lt;span class="nv"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;param&lt;/span&gt; &lt;span class="nv"&gt;$b&lt;/span&gt; &lt;span class="nv"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;result&lt;/span&gt; &lt;span class="nv"&gt;$i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;; load param $a onto the stack&lt;/span&gt;
    &lt;span class="nv"&gt;local.get&lt;/span&gt; &lt;span class="nv"&gt;$a&lt;/span&gt;

    &lt;span class="c1"&gt;; load param $b onto the stack&lt;/span&gt;
    &lt;span class="nv"&gt;local.get&lt;/span&gt; &lt;span class="nv"&gt;$b&lt;/span&gt;

    &lt;span class="c1"&gt;; perform 32-bit integer "add" operation&lt;/span&gt;
    &lt;span class="nv"&gt;i32.add&lt;/span&gt;

    &lt;span class="c1"&gt;; the last value on the stack is returned&lt;/span&gt;
    &lt;span class="c1"&gt;; which is the result of the `i32.add`&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;.wat&lt;/code&gt; file can be compiled to a &lt;code&gt;.wasm&lt;/code&gt; using &lt;code&gt;wat2wasm&lt;/code&gt; which is part of the &lt;a href="https://github.com/WebAssembly/wabt"&gt;WebAssembly Toolkit CLI tools&lt;/a&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;# outputs example.wasm&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; wat2wasm example.wat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the &lt;code&gt;.wasm&lt;/code&gt; can be executed from any host. It can even be executed from the command line using &lt;code&gt;wasmtime&lt;/code&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;# invoke "add" function, and pass args 1,2&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; wasmtime example.wasm &lt;span class="nt"&gt;--invoke&lt;/span&gt; add 1 2
3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  AssemblyScript
&lt;/h3&gt;

&lt;p&gt;There is also a higher-level language called &lt;a href="https://www.assemblyscript.org"&gt;AssemblyScript&lt;/a&gt;. It's like TypeScript for WebAssembly.&lt;/p&gt;

&lt;p&gt;If we re-write the &lt;code&gt;add()&lt;/code&gt; function from the previous section with AssemblyScript, it would look like this:&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="c1"&gt;// in add.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;u32&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;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&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;As you can see, it's much more readable now.&lt;/p&gt;

&lt;p&gt;To compile it, use AssemblyScript's compiler &lt;code&gt;asc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; assemblyscript
pnpm run asc add.ts &lt;span class="nt"&gt;--outFile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;math.wasm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Comparing formats
&lt;/h3&gt;

&lt;p&gt;To compare AssemblyScript to WAT, I built a little tool:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://assemblyscript-play.vercel.app"&gt;https://assemblyscript-play.vercel.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also use the CLI &lt;code&gt;wasm2wat&lt;/code&gt; to compare formats:&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;# outputs .wat format&lt;/span&gt;
wasm2wat math.wasm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Runtime execution
&lt;/h2&gt;

&lt;p&gt;Just like there are many ways to compile wasm, there are many ways to execute it too.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using WebAssembly in the browser
&lt;/h3&gt;

&lt;p&gt;To use the WebAssembly API in the browser, first load the assembly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// fetch .wasm file&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/path/to/some.wasm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// instantiate module with streaming&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;WebAssembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instantiateStreaming&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, call one of the exported functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An optional API can be passed into the module too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// fetch .wasm file&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/path/to/some.wasm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// instantiate module and pass an api&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;WebAssembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instantiateStreaming&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// share console.log&lt;/span&gt;
    &lt;span class="na"&gt;log&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="nx"&gt;log&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;
  
  
  Using WebAssembly on the server
&lt;/h3&gt;

&lt;p&gt;WebAssemblies can be executed on the server too. The API is virtually identical to the browser.&lt;/p&gt;

&lt;p&gt;The only difference is that instead of fetching the &lt;code&gt;.wasm&lt;/code&gt; from a server using a URL, it can be read from the disk using &lt;code&gt;fs.readFile()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&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;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// read .wasm file&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bytes&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;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/path/to/some.wasm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// instantiate the module&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;WebAssembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instantiate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, call one of the exported functions, just like we did in the browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's also possible to do this from many other languages. For example &lt;a href="https://docs.rs/wasmer/latest/wasmer"&gt;rust&lt;/a&gt;, &lt;a href="https://github.com/wasmerio/wasmer-ruby"&gt;ruby&lt;/a&gt;, &lt;a href="https://github.com/wasmerio/wasmer-python"&gt;python&lt;/a&gt; or from the &lt;a href="https://wasmtime.dev"&gt;CLI&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using WebAssembly in the Cloud
&lt;/h3&gt;

&lt;p&gt;Another big use-case for WASM is the cloud.&lt;/p&gt;

&lt;p&gt;It has some advantages over JavaScript cloud functions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No cold starts&lt;/strong&gt;: The host only has to load a &lt;code&gt;.wasm&lt;/code&gt; file instead of full app. Typical JS apps have many files to load, which takes a long time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faster deploys&lt;/strong&gt;: All that gets uploaded is a simple binary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Polyglot hosting&lt;/strong&gt;: All languages that compile to WASM can be deployed to the cloud without requiring any special runtime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Snapshots&lt;/strong&gt;: Execution can be snapshotted. For example, an app that does expensive work during initialization can be snapshotted. Then future requests can start with the snapshot, eliminating the expensive startup time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A great example of WASM in the cloud is &lt;a href="https://www.fermyon.com"&gt;Fermyon&lt;/a&gt;. It's like AWS Lambda but for WebAssembly.&lt;/p&gt;

&lt;p&gt;To use Fermyon, install their CLI &lt;a href="https://developer.fermyon.com/spin/install"&gt;spin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then create a new project:&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;# create a new spin project&lt;/span&gt;
&lt;span class="c"&gt;# template is "http-js"&lt;/span&gt;
&lt;span class="c"&gt;# project name is "spin-example"&lt;/span&gt;
spin new http-js spin-example
&lt;span class="nb"&gt;cd &lt;/span&gt;spin-example

&lt;span class="c"&gt;# install dependencies&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then define a handler in &lt;code&gt;src/index.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;encoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;TextEncoder&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&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;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;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;content-type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello World&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;buffer&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;To run in dev mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;spin watch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To deploy to The Cloud™, run &lt;code&gt;spin deploy&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;spin deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how that deploy was instant?&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotchas
&lt;/h2&gt;

&lt;p&gt;There are still a couple rough edges of of WASM:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WebAssembly is still kind of new and in active development. Though it is improving rapidly.&lt;/li&gt;
&lt;li&gt;Full support is not yet available for some programming languages.&lt;/li&gt;
&lt;li&gt;WASM doesn't have basic data types like strings or a standard library. This is by design. Languages are expected to be provide their own standard library.&lt;/li&gt;
&lt;li&gt;Because a "standard library" needs to live inside your &lt;code&gt;.wasm&lt;/code&gt;, it can make file size large.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most of these will be resolved with time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The future
&lt;/h2&gt;

&lt;p&gt;Over the past few years WebAssembly has made a lot of progress. &lt;/p&gt;

&lt;p&gt;Eventually all languages will have compilation targets and runtimes for hosting it (if they don't already). This will enable all languages to run in the browser, server, or even in hardware.&lt;/p&gt;

&lt;p&gt;It might also bring on new types of programming languages that are designed for a WebAssembly-first world.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webassembly</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Recurring payments with SvelteKit + Stripe</title>
      <dc:creator>Joshua Nussbaum</dc:creator>
      <pubDate>Mon, 10 Jul 2023 11:44:06 +0000</pubDate>
      <link>https://dev.to/joshnuss/recurring-payments-with-sveltekit-stripe-3d1j</link>
      <guid>https://dev.to/joshnuss/recurring-payments-with-sveltekit-stripe-3d1j</guid>
      <description>&lt;p&gt;When I built my last SaaS, I made &lt;strong&gt;one mistake&lt;/strong&gt;: I launched &lt;em&gt;without&lt;/em&gt; integrated payments.&lt;/p&gt;

&lt;p&gt;At the time, it didn't seem like the biggest unknown compared to building features. I figured it could always be added later.&lt;/p&gt;

&lt;p&gt;But it meant manually sending invoices and e-mail reminders and making phone calls about overdue payments. It wasn't that fun.&lt;/p&gt;

&lt;p&gt;In hindsight, I learned that payment is the validation that the SaaS is working. Pushing it off just delays that important signal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The good news is&lt;/strong&gt;: it's easy to avoid the mistake I made. Stripe subscriptions can be added to your SvelteKit site in an afternoon.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: An alternative method is Stripe Checkout. &lt;a href="https://dev.to/stripe/making-sense-of-stripe-checkout-payment-links-and-the-payment-element-23o5"&gt;Need help choosing?&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Payment flow
&lt;/h2&gt;

&lt;p&gt;For a customer to successfully pay for a subscription, the following steps are needed:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create a customer&lt;/strong&gt; record in Stripe.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a subscription&lt;/strong&gt; record in Stripe. It will have a &lt;code&gt;status&lt;/code&gt; of &lt;code&gt;incomplete&lt;/code&gt; until payment is completed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Display a payment form&lt;/strong&gt; using Stripe's &lt;code&gt;&amp;lt;PaymentElement/&amp;gt;&lt;/code&gt; component.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handle the payment form submission&lt;/strong&gt; and use Stripe's API to complete payment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handle webhooks&lt;/strong&gt; to provision the subscription.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Project configuration
&lt;/h2&gt;

&lt;p&gt;First, add these dependencies to your &lt;a href="https://kit.svelte.dev"&gt;SvelteKit&lt;/a&gt; app:&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;# `stripe` is the official node.js package.&lt;/span&gt;
&lt;span class="c"&gt;# `svelte-stripe` is a community maintained wrapper.&lt;/span&gt;
pnpm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; stripe svelte-stripe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then define environment variables in &lt;code&gt;.env&lt;/code&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;# Stripe secret key. Can be found at: https://dashboard.stripe.com/apikeys&lt;/span&gt;
&lt;span class="nv"&gt;SECRET_STRIPE_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sk_...

&lt;span class="c"&gt;# Stripe public key&lt;/span&gt;
&lt;span class="c"&gt;# The PUBLIC_ prefix allows Svelte to include it in client bundle&lt;/span&gt;
&lt;span class="nv"&gt;PUBLIC_STRIPE_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pk_...

&lt;span class="c"&gt;# domain to use for redirections&lt;/span&gt;
&lt;span class="nv"&gt;DOMAIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:5173
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll probably need to call the Stripe API from multiple places within your app, so it's a good idea to centralize the Stripe client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in src/lib/server/stripe.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Stripe&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;stripe&lt;/span&gt;&lt;span class="dl"&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;env&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;$env/dynamic/private&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// export the stripe instance&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;stripe&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;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SECRET_STRIPE_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// pin the api version&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="s1"&gt;2022-11-15&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;Then, any time you want to access Stripe, import &lt;code&gt;$lib/server/stripe&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// import singelton&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;stripe&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;$lib/server/stripe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// Do stuff with Stripe client:&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="c1"&gt;// stripe.resource.list(....)&lt;/span&gt;
&lt;span class="c1"&gt;// stripe.resource.create(....)&lt;/span&gt;
&lt;span class="c1"&gt;// stripe.resource.update(....)&lt;/span&gt;
&lt;span class="c1"&gt;// stripe.resource.retrieve(....)&lt;/span&gt;
&lt;span class="c1"&gt;// stripe.resource.del(....)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating the customer
&lt;/h2&gt;

&lt;p&gt;If your app has authentication, then you already have the customer's name and e-mail address. &lt;/p&gt;

&lt;p&gt;If not, you can display a form to capture it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- in src/routes/checkout/+page.svelte --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Checkout&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- posts to default form action --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Name"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"E-mail"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Continue&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then on the server-side, create the Stripe customer and store the customer id in your database. In our case, since there's no database, we'll place it in a cookie instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in src/routes/checkout/+page.server.js&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;stripe&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;$lib/stripe&lt;/span&gt;&lt;span class="dl"&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;redirect&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;@sveltejs/kit&lt;/span&gt;&lt;span class="dl"&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;actions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// default form action&lt;/span&gt;
  &lt;span class="na"&gt;default&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;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cookies&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;// get the form&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;form&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;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;// create the customer&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customer&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;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&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="c1"&gt;// set a cookie&lt;/span&gt;
    &lt;span class="nx"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customerId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;customer&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="c1"&gt;// redirect to collect payment&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;303&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/checkout/payment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating the subscription
&lt;/h2&gt;

&lt;p&gt;Now that we have a customer, the subscription can be created on their behalf.&lt;/p&gt;

&lt;p&gt;Subscriptions are keyed to a specific product and price. The product would be something like "Basic Plan" or "Enterprise Plan". Products can have multiple prices, for example the monthly price of the Basic Plan might be $20, but the yearly price is $100. Each are separate price IDs.&lt;/p&gt;

&lt;p&gt;It's best to store pricing in the database or in a config file, but for demo purposes, we'll pass it as a param in the URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in src/routes/checkout/payment/+page.server.js&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;stripe&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;$lib/stripe&lt;/span&gt;&lt;span class="dl"&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;env&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;$env/dynamic/private&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&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="nx"&gt;load&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="nx"&gt;cookies&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// pull customerId from cookie&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customerId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customerId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// pull priceId from URL&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;priceId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;priceId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// create the subscription&lt;/span&gt;
  &lt;span class="c1"&gt;// status is `incomplete` until payment succeeds&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subscription&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;subscriptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;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&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;priceId&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;payment_behavior&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;default_incomplete&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;payment_settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;save_default_payment_method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;on_subscription&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;expand&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;latest_invoice.payment_intent&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="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;clientSecret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;latest_invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payment_intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client_secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;returnUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/checkout/complete&lt;/span&gt;&lt;span class="dl"&gt;'&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;DOMAIN&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The endpoint returns two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;clientSecret&lt;/code&gt;: A token that represents the &lt;a href="https://dev.to/stripe/fundamentals-of-the-paymentintents-and-paymentmethods-apis-3646"&gt;payment intent&lt;/a&gt;. It's needed by Stripe Elements.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;returnURL&lt;/code&gt;: The URL of our thank-you page. The user will be redirected here once payment is successful.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Payment form
&lt;/h2&gt;

&lt;p&gt;In the payment form, the &lt;code&gt;&amp;lt;PaymentElement&amp;gt;&lt;/code&gt; component will handle displaying all the payment options. This allows the customer to pay with a credit card or any of the &lt;a href="https://stripe.com/payments/payment-methods"&gt;supported payment methods&lt;/a&gt; in their region.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- in src/routes/checkout/payment/+page.svelte --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;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;PUBLIC_STRIPE_KEY&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;$env/static/public&lt;/span&gt;&lt;span class="dl"&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;onMount&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;svelte&lt;/span&gt;&lt;span class="dl"&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;loadStripe&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;@stripe/stripe-js&lt;/span&gt;&lt;span class="dl"&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;Elements&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PaymentElement&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;svelte-stripe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

  &lt;span class="c1"&gt;// data from server&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;

  &lt;span class="c1"&gt;// destructure server data&lt;/span&gt;
  &lt;span class="nx"&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;clientSecret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;returnUrl&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// Stripe instance&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;

  &lt;span class="c1"&gt;// Stripe Elements instance&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt;

  &lt;span class="c1"&gt;// when component mounts&lt;/span&gt;
  &lt;span class="nx"&gt;onMount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// load the Stripe client&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;await&lt;/span&gt; &lt;span class="nx"&gt;loadStripe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PUBLIC_STRIPE_KEY&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 form submission&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ask Stripe to confirm the payment&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;error&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;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;confirmPayment&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="c1"&gt;// pass instance that was used to create the Payment Element&lt;/span&gt;
      &lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

      &lt;span class="c1"&gt;// specify where to send the user when payment succeeeds&lt;/span&gt;
      &lt;span class="na"&gt;confirmParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;return_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;returnUrl&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="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// handle error&lt;/span&gt;
      &lt;span class="nx"&gt;console&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;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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Payment&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

{#if stripe}
  &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;on:submit&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="na"&gt;preventDefault=&lt;/span&gt;&lt;span class="s"&gt;{submit}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- container for Stripe components --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Elements&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="na"&gt;stripe&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="na"&gt;clientSecret&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt; &lt;span class="na"&gt;bind:elements&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="c"&gt;&amp;lt;!-- display payment related fields --&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;PaymentElement&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Elements&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Pay&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
{:else}
  Loading Stripe...
{/if}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Completing the payment
&lt;/h2&gt;

&lt;p&gt;Stripe will send the user to our "thank you" page and the payment intent ID is passed along as a query string.&lt;/p&gt;

&lt;p&gt;The URL will look like this: &lt;code&gt;http://localhost:5173/payment/complete?payment_intent=pi_xyz123&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Using the payment intend ID, we can check on the payment's status and provision the account if it was successful.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in src/routes/checkout/complete/+page.server.js&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;stripe&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;$lib/stripe&lt;/span&gt;&lt;span class="dl"&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;redirect&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;@sveltejs/kit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&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="nx"&gt;load&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;// pull payment intent id from the URL query string&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;payment_intent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// ask Stripe for latest info about this paymentIntent&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;paymentIntent&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;paymentIntents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;retrieve&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="cm"&gt;/* Inspect the PaymentIntent `status` to indicate the status of the payment
   * to your customer.
   *
   * Some payment methods will [immediately succeed or fail][0] upon
   * confirmation, while others will first enter a `processing` state.
   *
   * [0] https://stripe.com/docs/payments/payment-methods#payment-notification
   */&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;message&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;paymentIntent&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="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="s1"&gt;succeeded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Success! Payment received.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

      &lt;span class="c1"&gt;// TODO: provision account here&lt;/span&gt;

      &lt;span class="k"&gt;break&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;processing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Payment processing. We'll update you when payment is received.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;requires_payment_method&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;// Redirect user back to payment page to re-attempt payment&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;303&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/checkout/payment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Something went wrong.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="k"&gt;break&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="nx"&gt;message&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;And a simple "thank you" page UI might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;--&lt;/span&gt; &lt;span class="na"&gt;in&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;checkout&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;complete&lt;/span&gt;&lt;span class="err"&gt;/+&lt;/span&gt;&lt;span class="na"&gt;page.svelte&lt;/span&gt; &lt;span class="na"&gt;--&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Checkout complete&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;{data.message}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Handling webhooks
&lt;/h2&gt;

&lt;p&gt;There's no guarantee the user makes it to the "thank you" page.&lt;/p&gt;

&lt;p&gt;It's possible they closed their browser or their internet cuts out because someone on their network is downloading too many torrents. That's why it's important to handle Stripe's webhooks too.&lt;/p&gt;

&lt;p&gt;In our SvelteKit app, we can add an endpoint to handle webhooks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in src/routes/stripe/webhooks/+server.js&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;stripe&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;$lib/stripe&lt;/span&gt;&lt;span class="dl"&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;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@sveltejs/kit&lt;/span&gt;&lt;span class="dl"&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;env&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;$env/dynamic/private&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// endpoint to handle incoming webhooks&lt;/span&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="nx"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// extract body&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&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;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="c1"&gt;// get the signature from the header&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&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="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;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;// var to hold event data&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;

  &lt;span class="c1"&gt;// verify the signature matches the body&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="nx"&gt;constructEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signature&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;SECRET_STRIPE_WEBHOOK_KEY&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;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// warn when signature is invalid&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;⚠️  Webhook signature verification failed.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&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="c1"&gt;// return, because signature is invalid&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid request&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="cm"&gt;/* Signature has been verified, so we can process events
   * 
   * Review important events for Billing webhooks:
   * https://stripe.com/docs/billing/webhooks
   */&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="nx"&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="s1"&gt;customer.subscription.created&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;// Subscription was created&lt;/span&gt;
      &lt;span class="c1"&gt;// Note: status will be `incomplete`&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customer.subscription.updated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;// Subscription has been changed&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;invoice.paid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;// Used to provision services after the trial has ended.&lt;/span&gt;
      &lt;span class="c1"&gt;// The status of the invoice will show up as paid. Store the status in your&lt;/span&gt;
      &lt;span class="c1"&gt;// database to reference when a user accesses your service to avoid hitting rate limits.&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;invoice.payment_failed&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 the payment fails or the customer does not have a valid payment method,&lt;/span&gt;
      &lt;span class="c1"&gt;//  an invoice.payment_failed event is sent, the subscription becomes past_due.&lt;/span&gt;
      &lt;span class="c1"&gt;// Use this webhook to notify your user that their payment has&lt;/span&gt;
      &lt;span class="c1"&gt;// failed and to retrieve new card details.&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customer.subscription.deleted&lt;/span&gt;&lt;span class="dl"&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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&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="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// handle a subscription canceled by your request&lt;/span&gt;
        &lt;span class="c1"&gt;// from above.&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// handle subscription canceled automatically based&lt;/span&gt;
        &lt;span class="c1"&gt;// upon your subscription settings.&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;// Unexpected event type&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// return a 200 with an empty JSON response&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;json&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;To test the webhook in development mode, a proxy is needed to tunnel events to our local machine. Since we're using localhost and not on the public internet.&lt;/p&gt;

&lt;p&gt;Fortunately, &lt;a href="https://stripe.com/cli"&gt;Stripe's CLI&lt;/a&gt; can do just that:&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;# login to your account&lt;/span&gt;
stripe login

&lt;span class="c"&gt;# forward webhook events to your localhost&lt;/span&gt;
stripe listen &lt;span class="nt"&gt;--forward-to&lt;/span&gt; localhost:5173/stripe/webhooks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;stripe listen&lt;/code&gt; will print out a "Webhook Secret" (it starts with &lt;code&gt;whsec_...&lt;/code&gt;). Make sure to add that to the &lt;code&gt;.env&lt;/code&gt; and restart the dev server.&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;# in .env&lt;/span&gt;
&lt;span class="nv"&gt;SECRET_STRIPE_WEBHOOK_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;whsec_...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it folks. You now have subscriptions in your SvelteKit app!&lt;/p&gt;

&lt;p&gt;For the complete source code, see:&lt;br&gt;
&lt;a href="https://github.com/joshnuss/sveltekit-stripe-subscriptions"&gt;https://github.com/joshnuss/sveltekit-stripe-subscriptions&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Subscriptions are the ultimate validation for your SaaS app. It's the proof there's demand for your idea.&lt;/p&gt;

&lt;p&gt;Without it, you'd have to resort to a more manual process and hoping people pay on time. It's better to get that validation as early as possible.&lt;/p&gt;

&lt;p&gt;Even if people don't buy, it's better to know that early, rather than hope it may happen later. At least then you can start pivoting.&lt;/p&gt;

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

</description>
      <category>svelte</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>stripe</category>
    </item>
    <item>
      <title>Using Prisma with SvelteKit</title>
      <dc:creator>Joshua Nussbaum</dc:creator>
      <pubDate>Tue, 27 Jun 2023 18:26:31 +0000</pubDate>
      <link>https://dev.to/joshnuss/using-prisma-with-sveltekit-46l6</link>
      <guid>https://dev.to/joshnuss/using-prisma-with-sveltekit-46l6</guid>
      <description>&lt;p&gt;Data is a critical part of every web app. Whether you're building a SvelteKit app to capture form inputs, visualize some data, or provide a dashboard to your users.&lt;/p&gt;

&lt;p&gt;But dealing with databases can be challenging. Each database has it's own slightly different APIs. You need to write a lot of database access code and keep database schemas up to date. That work adds up.&lt;/p&gt;

&lt;p&gt;That's why I like &lt;a href="https://prisma.io" rel="noopener noreferrer"&gt;Prisma&lt;/a&gt;, it's a developer friendly solution that simplifies many of the pain points of working with databases.&lt;/p&gt;

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

&lt;p&gt;Prisma is an ORM (object-relational mapper). It helps apps talk to databases. Kind of like a middleman between your JavaScript code and the underlying database.&lt;/p&gt;

&lt;p&gt;It can read rows from the database and turns them into JavaScript objects (hydration). And vice-versa, it can take JavaScript objects and update the database or create new records. And it supports &lt;a href="https://www.prisma.io/docs/reference/database-reference/supported-databases" rel="noopener noreferrer"&gt;many types of databases&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code generator
&lt;/h3&gt;

&lt;p&gt;In Prisma, models (aka tables) are defined in a file called &lt;code&gt;schema.prisma&lt;/code&gt;. Then you run &lt;code&gt;prisma generate&lt;/code&gt; to generate a custom client for your app.&lt;/p&gt;

&lt;p&gt;So you can think of Prisma as a kind of code generator.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F786f8h6qsrboq09v7grf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F786f8h6qsrboq09v7grf.png" alt="Prisma converts schema.prisma file into a client wrapper"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Schema syncing
&lt;/h3&gt;

&lt;p&gt;Prisma keeps the database schema up-to-date by syncing changes.&lt;/p&gt;

&lt;p&gt;It supports two styles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;push&lt;/code&gt;: In this style, the &lt;code&gt;schema.prisma&lt;/code&gt; is the source-of-truth. Additive changes, like adding a new table or a new column can be handled easily. It's the recommended approach during the prototype phase. But it's not ideal for more complex changes, like changing a data type.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;migrate&lt;/code&gt;: In this approach, the migration scripts are considered the source-of-truth. It provides a fine-grained control over schema changes. This is recommended for production apps. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.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%2F5yz8hbb9fxs77y9ws5bt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F5yz8hbb9fxs77y9ws5bt.png" alt="Schema syncing diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;To install the Prisma in a SvelteKit app, run:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

pnpm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; prisma @prisma/client


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then initialize the project:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

pnpm prisma init


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This does 2 things&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It creates a &lt;code&gt;prisma/schema.prisma&lt;/code&gt; where models can be defined.&lt;/li&gt;
&lt;li&gt;It adds an environment variable &lt;code&gt;DATABASE_URL&lt;/code&gt; to &lt;code&gt;.env&lt;/code&gt;. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;DATABASE_URL&lt;/code&gt; is the connection information Prisma needs to connect to the database.&lt;/p&gt;

&lt;p&gt;For example, for Postgres, it would look like &lt;code&gt;postgresql://USER:PASSWORD@HOST:PORT/DATABASE&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For a full list of examples, see &lt;a href="https://www.prisma.io/docs/reference/database-reference/connection-urls" rel="noopener noreferrer"&gt;Supported Connection URLs&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Starting points
&lt;/h3&gt;

&lt;p&gt;For new apps, the Prisma schema is written from scratch, but Prisma also supports importing an existing legacy database's schema too.&lt;/p&gt;

&lt;p&gt;To start with an &lt;strong&gt;existing database&lt;/strong&gt;, run &lt;code&gt;prisma db pull&lt;/code&gt; to pull the existing schema into &lt;code&gt;schema/prisma.schema&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To start with a &lt;strong&gt;new database&lt;/strong&gt;, define models in &lt;code&gt;prisma/schema.prisma&lt;/code&gt; and Prisma will sync the database schema for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modeling
&lt;/h2&gt;

&lt;p&gt;Here are some examples of models and indexes.&lt;/p&gt;

&lt;p&gt;To define a basic model:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

// in prisma/schema.prisma
model Product {
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And fields can be added in the format &lt;code&gt;name DataType&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

model Product {
  // auto-incrementing primary key
  id Int @id @default(autoincrement())
  barcode String
  name String
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To add an index:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

model Product {
  // ...

  // define a unique index
  @@unique([barcode])

  // define a non-unique index
  @@index([name])
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For more examples, see the &lt;a href="https://www.prisma.io/docs/concepts/components/prisma-schema/data-model" rel="noopener noreferrer"&gt;docs on modelling&lt;/a&gt; or my course on &lt;a href="https://bit.ly/svelte-prisma" rel="noopener noreferrer"&gt;Svelte + Prisma&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Syncing the database
&lt;/h2&gt;

&lt;p&gt;To update the database schema, run:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

pnpm prisma db push


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: In some cases, a conflict can occur. For example, if a field's data type is changed from &lt;code&gt;String&lt;/code&gt; to &lt;code&gt;Int&lt;/code&gt;, there is no automatic way to reconcile that conversion. Prisma will notify you that there is a conflict and give you the option to reset the table (causing data loss), or you can use migrations for more control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browsing your database
&lt;/h2&gt;

&lt;p&gt;Prisma comes with a handy database viewer that runs in the browser. It's a useful tool for inspecting database tables during development.&lt;/p&gt;

&lt;p&gt;Here's a screenshot:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fyh9k7zf7gq55nm015jzt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fyh9k7zf7gq55nm015jzt.png" alt="Screenshot of Prisma Studio"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To view it, run: &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

pnpm prisma studio


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then visit &lt;code&gt;https://localhost:5555&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessing the database
&lt;/h2&gt;

&lt;p&gt;It's likely that many places in your app will need to access the database. So, it's a good idea to centralize the db client.&lt;/p&gt;

&lt;p&gt;To do that, create a file called &lt;code&gt;src/lib/db.server.js&lt;/code&gt; and instantiate the client:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PrismaClient&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;@prisma/client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// expose a singleton&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;db&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;PrismaClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now, anytime a connection is needed, import &lt;code&gt;lib/db.server.js&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// import the centralized connection&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;db&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;$lib/db.server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// then do stuff with the db connection&lt;/span&gt;
&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&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="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;span class="c1"&gt;// etc...&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Manual disconnection is rarely needed because Prisma automatically closes connections when the process ends.&lt;/p&gt;

&lt;h2&gt;
  
  
  Seeding data
&lt;/h2&gt;

&lt;p&gt;It's much easier to set up developer machines when the app ships with a seed dataset. It also makes it easier to reset the environment when if it ever gets hosed.&lt;/p&gt;

&lt;p&gt;In Prisma, seed data is defined in &lt;code&gt;prisma/seed.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's an example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// in prisma/seed.js&lt;/span&gt;

&lt;span class="c1"&gt;// import db client from previous step&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;db&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;$lib/db.server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// create one record&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;product&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;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;barcode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1234&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Shirt&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="c1"&gt;// or create records in bulk&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;barcode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;5678&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Pants&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;barcode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;91011&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Socks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then add a &lt;code&gt;prisma&lt;/code&gt; section to &lt;code&gt;package.json&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"prisma"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"seed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vite-node ./prisma/seed.js"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;code&gt;vite-node&lt;/code&gt; is used instead of &lt;code&gt;node&lt;/code&gt; because our &lt;code&gt;seed.js&lt;/code&gt; imports a &lt;em&gt;vite&lt;/em&gt; alias &lt;code&gt;$lib&lt;/code&gt;. So make sure to install &lt;code&gt;vite-node&lt;/code&gt; too:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

pnpm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; vite-node


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then to load the seed data:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;p&gt;pnpm prisma db seed&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Conclusion&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Prisma is a great option for connecting SvelteKit projects with a database.&lt;/p&gt;

&lt;p&gt;Prisma handles all the tricky stuff, like syncing your database schema and generating SQL queries, so you don't have to do that by hand.&lt;/p&gt;

&lt;p&gt;It frees up developer time to focus on building features.&lt;/p&gt;

&lt;p&gt;P.S. For a SvelteKit/Prisma repo with more examples, see &lt;a href="https://github.com/joshnuss/markdown-mail" rel="noopener noreferrer"&gt;joshnuss/markdown-mail&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>svelte</category>
      <category>javascript</category>
      <category>database</category>
      <category>webdev</category>
    </item>
    <item>
      <title>React to Svelte Cheatsheet</title>
      <dc:creator>Joshua Nussbaum</dc:creator>
      <pubDate>Tue, 08 Mar 2022 16:20:08 +0000</pubDate>
      <link>https://dev.to/joshnuss/react-to-svelte-cheatsheet-1a2a</link>
      <guid>https://dev.to/joshnuss/react-to-svelte-cheatsheet-1a2a</guid>
      <description>&lt;p&gt;Are you coming to &lt;strong&gt;Svelte&lt;/strong&gt; from a &lt;strong&gt;React&lt;/strong&gt; background?&lt;/p&gt;

&lt;p&gt;Here is a cheat sheet that lists the &lt;strong&gt;similarities&lt;/strong&gt; and &lt;strong&gt;differences&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ar2fed9ybg1jl9f6tjka.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.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%2Far2fed9ybg1jl9f6tjka.png" alt="Cheatsheet"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ar2fed9ybg1jl9f6tjka.png" rel="noopener noreferrer"&gt;Full size image&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://drive.google.com/file/d/1Kjjp_Z0HcH-NxquV0bo6vu2IxkVmU0Dt/view" rel="noopener noreferrer"&gt;PDF Version&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Want more?
&lt;/h1&gt;

&lt;p&gt;If you'd like to learn more about Svelte, check out my &lt;a href="https://joshuanussbaum.podia.com" rel="noopener noreferrer"&gt;short video courses&lt;/a&gt; ✨&lt;/p&gt;

</description>
      <category>svelte</category>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How is Svelte different than React?</title>
      <dc:creator>Joshua Nussbaum</dc:creator>
      <pubDate>Fri, 05 Mar 2021 02:41:00 +0000</pubDate>
      <link>https://dev.to/joshnuss/why-svelte-59a5</link>
      <guid>https://dev.to/joshnuss/why-svelte-59a5</guid>
      <description>&lt;p&gt;To get a better understanding of what Svelte brings us, it helps to step back and look at how we got here:&lt;/p&gt;

&lt;p&gt;Back in the 90s, in the original version of the web, there was only HTML. Browsers displayed static documents without any interactivity. The only way to get updated information, was by reloading the page, or navigating to a new page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Netscape and Internet Explorer
&lt;/h2&gt;

&lt;p&gt;In 1995, &lt;a href="https://en.wikipedia.org/wiki/JavaScript#History"&gt;Netscape released JavaScript&lt;/a&gt;, making it possible to execute code on the end-user's machine. &lt;/p&gt;

&lt;p&gt;Now we could do things like: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read a value from the DOM
&lt;code&gt;document.getElementById(...).innerText&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Write a value to the DOM: &lt;code&gt;document.getElemetById(...).innerText = ...&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Get notified when something happens: &lt;code&gt;&amp;lt;button onclick="alert(1995)"&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As developers began experimenting with this newfangled JavaScript thing, they found one aspect really tough: dealing with the differences between browsers. Both Netscape Navigator and Internet Explorer did things in their own way, making developers' responsible for handling those inconsistencies.&lt;/p&gt;

&lt;p&gt;The result was code like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;isNetscape&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isIE&lt;/span&gt;

&lt;span class="c1"&gt;// figure out which browser this is&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appVersion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;isNetscape&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appName&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Netscape&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;isIE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Microsoft&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// branch based on browser type&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;isIE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// The Internet Explorer Way™&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;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;isNetscape&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// The Netscape Way™&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This kind of browser detection code littered codebases everywhere. The extra branching was a nuisance, like a cognitive tax, making code harder to read and maintain. Translation: not fun.&lt;/p&gt;

&lt;h2&gt;
  
  
  jQuery
&lt;/h2&gt;

&lt;p&gt;In 2006, John Resig released a compatibility layer called &lt;a href="https://en.wikipedia.org/wiki/JQuery#History"&gt;jQuery&lt;/a&gt;. It was a way to interact with the DOM without being an expert on browser feature matrices. It completely solved the inconsistency issue. No more &lt;code&gt;if (isNetscape)&lt;/code&gt; or &lt;code&gt;if (isIE)&lt;/code&gt; conditions!&lt;/p&gt;

&lt;p&gt;Instead, we could interact with the page using CSS selectors, and jQuery dealt with the browser on our behalf.&lt;/p&gt;

&lt;p&gt;It looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// read state&lt;/span&gt;
&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form input#email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// write state&lt;/span&gt;
&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello World!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// get notified when something changes&lt;/span&gt;
&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2006&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;But there were some challenges here too:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Selectors&lt;/strong&gt;: If the structure of the markup changed - which happens a lot - it can break the CSS selector. For example, if you had a selector &lt;code&gt;$('h1')..&lt;/code&gt;, then you change the markup from &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; to an &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt;, your selector just silently stops working, and you won't know until a user complains. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Syncing state&lt;/strong&gt;: State changes happen in 2 directions, DOM-to-model and model-to-DOM. jQuery didn't provide any tooling here, leaving developers responsible for managing the synchronization logic on their own.
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  AngularJS
&lt;/h2&gt;

&lt;p&gt;In 2010, Google launched &lt;a href="https://en.wikipedia.org/wiki/AngularJS"&gt;AngularJS 1.x&lt;/a&gt;, a framework that helps with state management.&lt;/p&gt;

&lt;p&gt;Instead of writing jQuery code, like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;someExpression&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expressions (called bindings) could be embedded directly inside the HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;{{someExpression}}&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and Angular would sync those bindings for us.&lt;/p&gt;

&lt;p&gt;Later, if we change our HTML, say by switching an &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; to an &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt;, nothing breaks with the Angular version. There's no CSS selectors to update.&lt;/p&gt;

&lt;p&gt;AngularJS components looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- specify where our controller/component mounts to --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;ng-app=&lt;/span&gt;&lt;span class="s"&gt;"myApp"&lt;/span&gt; &lt;span class="na"&gt;ng-controller=&lt;/span&gt;&lt;span class="s"&gt;"myCtrl"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- binding to a variable --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;{{year}}&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;// declare a module&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;angular&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myApp&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="c1"&gt;// declare a controller&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myCtrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$scope&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// update a variable and trigger syncing&lt;/span&gt;
  &lt;span class="nx"&gt;$scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;year&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2010&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The magic was that anytime you changed something on the &lt;code&gt;$scope&lt;/code&gt; variable, Angular would go thru a "digestion cycle", that recursively updated all the bindings.&lt;/p&gt;

&lt;p&gt;But there were some problems here too:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It only worked in the browser&lt;/strong&gt;: If a search engine crawled the page, it would see

&lt;code&gt;&amp;lt;h1&amp;gt;{{someExpression}}&amp;lt;/h1&amp;gt;&lt;/code&gt;

. Unless you're trying to rank high for

&lt;code&gt;{{someExpression}}&lt;/code&gt;

, that's not great.&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The digestion loop was inefficient&lt;/strong&gt;: It takes time to walk the DOM tree and apply changes. Sometimes it could take multiple passes for all the values to settle.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  React
&lt;/h2&gt;

&lt;p&gt;In 2013, Facebook &lt;a href="https://en.wikipedia.org/wiki/React_(JavaScript_library)#History"&gt;launched React&lt;/a&gt;, a library for syncing state with UI.&lt;/p&gt;

&lt;p&gt;It solved some issues that AngularJS 1.x had. It's isomorphic, it can render HTML both on the server and in the browser, fixing the SEO problem. It also implemented a more efficient syncing algorithm called &lt;a href="https://reactjs.org/docs/faq-internals.html"&gt;Virtual DOM&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Refresher: Virtual DOM keeps a copy of the DOM in memory. It uses the copy to figure out what changes (the delta), while limiting potentially slow interactions with the browser DOM. (Though it's been pointed out that &lt;a href="https://svelte.dev/blog/virtual-dom-is-pure-overhead"&gt;this may be overhead&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;It's still conceptually similar to AngularJS, from a state management perspective. React's &lt;code&gt;setState({value})&lt;/code&gt; or in more recently, the &lt;code&gt;useState()&lt;/code&gt; hook, is roughly equivalent to Angular's &lt;code&gt;$scope.value = value&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Hook example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// React state with hooks&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;year&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setYear&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// setting state&lt;/span&gt;
&lt;span class="c1"&gt;// functionally equivalent to AngularJS's&lt;/span&gt;
&lt;span class="c1"&gt;// `$scope.year = 2017`&lt;/span&gt;
&lt;span class="nx"&gt;setYear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2017&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The problem
&lt;/h3&gt;

&lt;p&gt;React relies on developers to signal when things change. That  means writing lots of Hook code. But Hooks aren't trivial to write, they come with a bunch of &lt;a href="https://reactjs.org/docs/hooks-rules.html"&gt;rules&lt;/a&gt;, and those rules introduce a &lt;a href="https://github.com/joshnuss/react-hooks-in-svelte"&gt;extra cognitive load into our codebases&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Svelte
&lt;/h2&gt;

&lt;p&gt;In 2019, &lt;a href="https://github.com/Rich-Harris"&gt;Rich Harris&lt;/a&gt; released Svelte3. The idea behind Svelte is: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if a compiler could determine when state changes?&lt;/strong&gt;&lt;br&gt;
That could save developers a lot of time.&lt;/p&gt;

&lt;p&gt;It turns out to be a &lt;strong&gt;really good idea&lt;/strong&gt;. Being a compiler, Svelte can find all the places where our code changes state, and update the UI for us.&lt;/p&gt;
&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;Say we assign a variable inside a Svelte component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- a .svelte component --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;// assign a value&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;year&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2019&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- this &amp;lt;h1&amp;gt; depends on `year` state --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello {year}!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Svelte detects the &lt;code&gt;let&lt;/code&gt; statement and starts tracking the variable. If we change it later, say &lt;code&gt;year = 2021&lt;/code&gt;, Svelte sees the assignment &lt;code&gt;=&lt;/code&gt; as a state change and updates all the places in the UI that depend on that binding. &lt;/p&gt;

&lt;p&gt;Svelte is writing all the Hooks code for us! &lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;If you think about it, a big part of a developer's job is organizing state, moving state back and forth between the UI and the model. It takes effort, and it's tricky to get right. By offloading some of that work to compile-time tools, we can &lt;strong&gt;save a lot of time and energy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Another side effect is, we end up with &lt;strong&gt;less code&lt;/strong&gt;. That makes our programs smaller, clearer to read, &lt;strong&gt;easier to maintain&lt;/strong&gt;, cheaper to build, and most importantly: &lt;strong&gt;more fun&lt;/strong&gt; to work with.&lt;/p&gt;

&lt;h1&gt;
  
  
  Want more?
&lt;/h1&gt;

&lt;p&gt;If you'd like to learn more about Svelte, check out my &lt;a href="https://joshuanussbaum.podia.com"&gt;short video courses&lt;/a&gt; ✨&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>svelte</category>
    </item>
    <item>
      <title>Declarative controllers with Phoenix</title>
      <dc:creator>Joshua Nussbaum</dc:creator>
      <pubDate>Tue, 24 Nov 2020 04:09:33 +0000</pubDate>
      <link>https://dev.to/joshnuss/declarative-controllers-with-phoenix-1gng</link>
      <guid>https://dev.to/joshnuss/declarative-controllers-with-phoenix-1gng</guid>
      <description>&lt;p&gt;The other day I found myself needing to write a bunch of controllers. Maybe I was feeling lazy, but I really wanted to finish the work quickly. I thought about how I could wrap it up in one afternoon.&lt;/p&gt;

&lt;p&gt;Maybe I could write a code generator? or some kind of library?&lt;/p&gt;

&lt;p&gt;I decided to go the library route.&lt;/p&gt;

&lt;p&gt;In the end, it allowed me to write about 10 controllers in a couple of hours, so I'm sharing it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Anatomy of a controller
&lt;/h1&gt;

&lt;p&gt;A controller action does 3 things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Input&lt;/strong&gt;: Passing &lt;code&gt;params&lt;/code&gt; and data from &lt;code&gt;conn&lt;/code&gt; to a context function.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Algorithm&lt;/strong&gt;: the context function.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output&lt;/strong&gt;: Rendering a response based on the context function's result.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's use this &lt;code&gt;create&lt;/code&gt; action as an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# call context function&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;Catalog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assigns&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# pattern match result&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="c1"&gt;# rendering success&lt;/span&gt;
      &lt;span class="n"&gt;conn&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;put_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:created&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"show.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="c1"&gt;# rendering error&lt;/span&gt;
      &lt;span class="n"&gt;conn&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;put_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:unprocessable_entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"error.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my case, all my controller actions would follow this structure.&lt;/p&gt;

&lt;h1&gt;
  
  
  Reusable logic
&lt;/h1&gt;

&lt;p&gt;Each controller action is calling a context function and then pattern matching against the result. The result is always a tagged tuple like &lt;code&gt;{:ok, data}&lt;/code&gt; or &lt;code&gt;{:error, changeset}&lt;/code&gt;. So it seems the response handling could be shared between controllers. &lt;/p&gt;

&lt;p&gt;We can extract the shared logic to a module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Responder&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# handle success&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;put_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:created&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"show.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;record:&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# handle error&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;put_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:unprocessable_entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"error.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;changeset:&lt;/span&gt; &lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt; 
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then our controller code can be simplified:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# import shared response logic&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Responder&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# call context&lt;/span&gt;
  &lt;span class="no"&gt;Catalog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assigns&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# re-use response logic&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# call context&lt;/span&gt;
  &lt;span class="no"&gt;Catalog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assigns&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# re-use response logic&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Way less repetition, it's starting to shape up.&lt;/p&gt;

&lt;h1&gt;
  
  
  A pattern emerges
&lt;/h1&gt;

&lt;p&gt;All the actions now have a similar look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So why not distill it further with a macro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmacro&lt;/span&gt; &lt;span class="n"&gt;defaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;quote&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="kn"&gt;unquote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;conn:&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;params:&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;assigns:&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assigns&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kn"&gt;unquote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fun&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, replace &lt;code&gt;def action_name(...)&lt;/code&gt; with &lt;code&gt;defaction :action_name, ...&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;defaction&lt;/span&gt; &lt;span class="ss"&gt;:index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;Catalog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list_products&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;defaction&lt;/span&gt; &lt;span class="ss"&gt;:show&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;Catalog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;defaction&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;Catalog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"product"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;defaction&lt;/span&gt; &lt;span class="ss"&gt;:update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;Catalog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update_product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"product"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;defaction&lt;/span&gt; &lt;span class="ss"&gt;:delete&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;Catalog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete_product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now our controllers are completely declarative. Easier to read and less typing :)&lt;/p&gt;

&lt;h1&gt;
  
  
  Hex package
&lt;/h1&gt;

&lt;p&gt;Since it worked out well for me, I open sourced it: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/joshnuss/transponder"&gt;https://github.com/joshnuss/transponder&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Hex package: &lt;a href="https://hex.pm/packages/transponder"&gt;https://hex.pm/packages/transponder&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To set it up, install the hex package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# in mix.exs, add to `deps`:&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:transponder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 0.2"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in any controller, import &lt;code&gt;Transponder&lt;/code&gt;, passing the required format (JSON or HTML).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;ProductsController&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:controller&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Transponder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;format:&lt;/span&gt; &lt;span class="no"&gt;Transponder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;JSON&lt;/span&gt;

  &lt;span class="n"&gt;defaction&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;The idea isn't a new one, it exists in Rails with &lt;a href="https://github.com/hcatlin/make_resourceful"&gt;&lt;code&gt;make_resourceful&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/jamesgolick/resource_controller"&gt;&lt;code&gt;resource_controller&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The approach works well for controllers that follow the same  pattern. It keeps controller code declarative, increases readability, and eliminates the need for testing controllers. &lt;/p&gt;

&lt;p&gt;Of course for more intricate applications, hand-tuned response logic may work better. Nothing wrong with that.&lt;/p&gt;

&lt;p&gt;P.S. It's still alpha software, but feel free to give it a try and open PRs&lt;/p&gt;

&lt;p&gt;Happy hacking,&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>phoenix</category>
    </item>
    <item>
      <title>Create a blog with Sapper &amp; Markdown: Tagging</title>
      <dc:creator>Joshua Nussbaum</dc:creator>
      <pubDate>Thu, 29 Oct 2020 06:58:21 +0000</pubDate>
      <link>https://dev.to/joshnuss/create-a-blog-with-sapper-markdown-part-2-31m4</link>
      <guid>https://dev.to/joshnuss/create-a-blog-with-sapper-markdown-part-2-31m4</guid>
      <description>&lt;p&gt;When dealing with lots of content, it's a good idea to have some categorization. One versatile approach to categorization is tagging. Nearly all blogs use it, and ours will too.&lt;/p&gt;

&lt;p&gt;Note: If you prefer to watch rather than read, there's a screencast version here: &lt;a href="https://youtu.be/PGLsFfBf1UA"&gt;https://youtu.be/PGLsFfBf1UA&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding metadata
&lt;/h2&gt;

&lt;p&gt;Continuing from &lt;a href="https://dev.to/joshnuss/create-a-blog-with-markdown-sapper-50ad"&gt;Part 1&lt;/a&gt;, our blog repo has a &lt;code&gt;/posts&lt;/code&gt; folder filled with Markdown files. Our first step will be to add a new field called &lt;code&gt;tags&lt;/code&gt; to the "front matter" metadata in each markdown file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;---
title: Example Post
date: 2020-10-28
&lt;/span&gt;&lt;span class="gi"&gt;+tags: hello, world
&lt;/span&gt;&lt;span class="p"&gt;---
&lt;/span&gt;
# Example Title

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Parsing tags
&lt;/h2&gt;

&lt;p&gt;Sine the &lt;code&gt;tags&lt;/code&gt; string is comma-delimited, it will need to be converted into to an array during the parsing phase.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in src/posts.js&lt;/span&gt;
&lt;span class="c1"&gt;// transform is called once for each post&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;transform&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="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;

  &lt;span class="c1"&gt;// the `tags` field is optional, so default to empty list&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

  &lt;span class="c1"&gt;// check if `tags` field is defined on this post&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;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// split the tags string by comma, and trim away any extra spaces&lt;/span&gt;
    &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="o"&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;tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// return previous data and tags &lt;/span&gt;
  &lt;span class="k"&gt;return&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;tags&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;h1&gt;
  
  
  Listing tags
&lt;/h1&gt;

&lt;p&gt;Now that each post has a cleaned up &lt;code&gt;tags&lt;/code&gt; array. It's ready to be displayed on the &lt;code&gt;/post/:permalink&lt;/code&gt; page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;&amp;lt;!-- src/routes/posts/[permalink].svelte --&amp;gt;
&amp;lt;script&amp;gt;...&amp;lt;/script&amp;gt;
&lt;/span&gt;
&amp;lt;h1&amp;gt;{post.title}&amp;lt;/h1&amp;gt;

&amp;lt;!-- new component to show tag list --&amp;gt;
&lt;span class="gi"&gt;+&amp;lt;Tags tags={post.tags}/&amp;gt;
&lt;/span&gt;
{@html post.html}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the new &lt;code&gt;&amp;lt;Tags/&amp;gt;&lt;/code&gt; component will be defined as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- src/components/Tags.svelte --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;// take in tags as a prop&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;tags&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- iterate through each tag --&amp;gt;&lt;/span&gt;
  {#each tags as tag}
    &lt;span class="c"&gt;&amp;lt;!-- link each tag to /tag/:tag page --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/tag/{tag}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;#{tag}&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  {/each}
&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Listing posts by tag
&lt;/h2&gt;

&lt;p&gt;To show all the posts for a given tag, we'll need a new page and some JavaScript logic to filter posts by tag.&lt;/p&gt;

&lt;p&gt;First, lets define the &lt;code&gt;findByTag()&lt;/code&gt; function. It will take the &lt;code&gt;tag&lt;/code&gt; as a parameter and return the list of posts matching the tag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/posts.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;findByTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// filter out all posts that don't include the tag&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&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;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tag&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;Then, define new page for &lt;code&gt;/tag/:tag&lt;/code&gt; that will use &lt;code&gt;findByTag()&lt;/code&gt; to locate posts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- src/routes/tag/[tag].svelte --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;context=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;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;findByTag&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;@/posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// extract tag param&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;tag&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;
    &lt;span class="c1"&gt;// find posts based on tag param&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;findByTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;// return props&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;// props are provided by preload() function&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- show #tag as title --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;#{tag}&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

{#each posts as post}
  &lt;span class="c"&gt;&amp;lt;!-- show each post --&amp;gt;&lt;/span&gt;
{/each}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Extracting a  component
&lt;/h2&gt;

&lt;p&gt;Lastly, since we are showing the list of posts on 2 pages &lt;code&gt;/tag/:tag&lt;/code&gt; and &lt;code&gt;/post/:permalink&lt;/code&gt;, it would be to avoid duplication and have a reusable component for listing posts. This will make it easier to adjust and style down the road.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- src/components/PostList.svelte --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;// it takes an array of posts as a prop&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- iterate thru each post and output an &amp;lt;article&amp;gt; tag --&amp;gt;&lt;/span&gt;
{#each posts as post}
  &lt;span class="nt"&gt;&amp;lt;article&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- link to /posts/:permalink page --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;{`/posts/${post.permalink}`}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;{post.title}&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;{post.summary}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
{/each}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then update the pages &lt;code&gt;src/routes/posts/[permalink].svelte&lt;/code&gt; and &lt;code&gt;src/routes/tag/[tag].svelte&lt;/code&gt; to use &lt;code&gt;&amp;lt;PostList/&amp;gt;&lt;/code&gt; instead of  &lt;code&gt;{#each}&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- use our new shiny component --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PostList&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="na"&gt;posts&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Our little blog is getting better! Now that we have tagging working, in the next post we'll explore adding syntax highlighting.&lt;/p&gt;

&lt;p&gt;You can find all the code here:&lt;br&gt;
&lt;a href="https://github.com/joshnuss/blog-template"&gt;https://github.com/joshnuss/blog-template&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy coding! ✌&lt;/p&gt;
&lt;h1&gt;
  
  
  Screencast
&lt;/h1&gt;

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

</description>
      <category>svelte</category>
      <category>javascript</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
