<?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: Simon Knott</title>
    <description>The latest articles on DEV Community by Simon Knott (@skn0tt).</description>
    <link>https://dev.to/skn0tt</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%2F169946%2Fc4702198-0f32-4f1b-96b7-e19b6c065392.jpg</url>
      <title>DEV Community: Simon Knott</title>
      <link>https://dev.to/skn0tt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/skn0tt"/>
    <language>en</language>
    <item>
      <title>Iterate quickly using the new --only-changed option</title>
      <dc:creator>Simon Knott</dc:creator>
      <pubDate>Wed, 21 Aug 2024 07:14:15 +0000</pubDate>
      <link>https://dev.to/playwright/iterate-quickly-using-the-new-only-changed-option-55m2</link>
      <guid>https://dev.to/playwright/iterate-quickly-using-the-new-only-changed-option-55m2</guid>
      <description>&lt;p&gt;Playwright &lt;a href="https://github.com/microsoft/playwright/releases/tag/v1.46.0" rel="noopener noreferrer"&gt;v1.46&lt;/a&gt; ships with a nifty new feature for local development.&lt;br&gt;
By specifying the &lt;code&gt;--only-changed&lt;/code&gt; option, Playwright looks at uncommited changes and runs all affected test files.&lt;/p&gt;

&lt;p&gt;If you've used Jest before, this is pretty similar to Jest's &lt;a href="https://jestjs.io/docs/cli#--onlychanged" rel="noopener noreferrer"&gt;&lt;code&gt;--onlyChanged&lt;/code&gt; flag&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's see it in action. If you prefer video, watch the Demo in the &lt;a href="https://youtu.be/tQo7w-QQBsI?si=Jm-tANkKIkf5ElW5&amp;amp;t=110" rel="noopener noreferrer"&gt;release video&lt;/a&gt;. In this example, we have a repository without uncommitted changes:&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;// utils.ts&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;inputPlaceholder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;What needs to be done?&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;todoItemTestID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todo-item&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;todo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;buy milk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// test.spec.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;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&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;@playwright/test&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;inputPlaceholder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;todoItemTestID&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;./utils.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;adding a todo&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;page&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://demo.playwright.dev/todomvc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inputField&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="nf"&gt;getByPlaceholder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputPlaceholder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;inputField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;inputField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;press&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Enter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputField&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeEmpty&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&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="nf"&gt;getByTestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todoItemTestID&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toHaveText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&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;If we run a test with &lt;code&gt;--only-changed&lt;/code&gt;, we see that no tests are run:&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;$ &lt;/span&gt;npx playwright &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--only-changed&lt;/span&gt;
Error: No tests found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's make a change to one of the files, but not commit it yet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;// utils.ts
&lt;span class="p"&gt;export const inputPlaceholder = 'What needs to be done?'
export const todoItemTestID = 'todo-item'
&lt;/span&gt;&lt;span class="gd"&gt;- export const todo = 'buy milk'
&lt;/span&gt;&lt;span class="gi"&gt;+ export const todo = 'make cappucino'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ npx playwright &lt;span class="nb"&gt;test

&lt;/span&gt;Running 1 &lt;span class="nb"&gt;test &lt;/span&gt;using 1 worker

  ✓  1 &lt;span class="o"&gt;[&lt;/span&gt;chromium] › test.spec.ts:4:5 › adding a todo &lt;span class="o"&gt;(&lt;/span&gt;426ms&lt;span class="o"&gt;)&lt;/span&gt;

  1 passed &lt;span class="o"&gt;(&lt;/span&gt;837ms&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Playwright found changes in &lt;code&gt;utils.ts&lt;/code&gt;, so it executed all test files that depend on &lt;code&gt;utils.ts&lt;/code&gt;. Wonderful!&lt;/p&gt;

&lt;p&gt;By default, Playwright will compare against &lt;a href="https://git-scm.com/book/en/v2/Git-Internals-Git-References#ref_the_ref" rel="noopener noreferrer"&gt;&lt;code&gt;HEAD&lt;/code&gt;&lt;/a&gt; to determine changed files.&lt;br&gt;
But you can also specify a different Git revision to compare against: Using &lt;code&gt;--only-changed=main&lt;/code&gt; on a feature branch executes all tests that are affected by changes on the feature branch. And &lt;code&gt;--only-changed=&amp;lt;commit sha&amp;gt;&lt;/code&gt; executes all changes between &lt;code&gt;HEAD&lt;/code&gt; and the specified commit.&lt;/p&gt;
&lt;h2&gt;
  
  
  How do I use this?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;--only-changed&lt;/code&gt; is another tool in the toolbelt to help you iterate quickly. Here's five ideas for how you can integrate it into your workflow:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Local development&lt;/strong&gt;: Use &lt;code&gt;--only-changed=main&lt;/code&gt; to quickly run affected tests as you make changes. This is especially useful when you're working on a feature and want to see the impact of your changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. pre-commit hook&lt;/strong&gt;: Running with &lt;code&gt;--only-changed&lt;/code&gt; in a &lt;a href="https://git-scm.com/docs/githooks#_pre_commit" rel="noopener noreferrer"&gt;pre-commit hook&lt;/a&gt; prevents failing tests from being commited. Here's an example hook you can use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx playwright &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--only-changed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. pre-push hook&lt;/strong&gt;: As the name says, a &lt;a href="https://git-scm.com/docs/githooks#_pre_push" rel="noopener noreferrer"&gt;pre-push hook&lt;/a&gt; runs before pushing. This prevents failing tests from invoking a costly CI run. Here's an example implementation:&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="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read &lt;/span&gt;local_ref local_sha remote_ref remote_sha
&lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c"&gt;# deleting branch, nothing to check&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$local_sha&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'0000000000000000000000000000000000000000'&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi

    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Running Playwright for HEAD...&lt;/span&gt;&lt;span class="nv"&gt;$remote_sha&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    npx playwright &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--only-changed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$remote_sha&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Faster feedback in CI&lt;/strong&gt;: Prior to a full run, use &lt;code&gt;--only-changed&lt;/code&gt; to get faster feedback on new failures. This is especially useful in large test suites, where running all tests can take a long time. Here's how that'd look in GitHub Actions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;...
    - name: Install Playwright Browsers
      run: npx playwright install --with-deps
&lt;span class="gi"&gt;+   - name: Run changed Playwright tests
+     run: npx playwright test --only-changed=${{ github.base_ref }}
&lt;/span&gt;    - name: Run Playwright tests
      run: npx playwright test
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Bonus idea&lt;/strong&gt;: If you're interested in &lt;code&gt;--only-changed&lt;/code&gt;, you might also be interested in Playwright's &lt;a href="https://playwright.dev/docs/test-ui-mode#watch-mode" rel="noopener noreferrer"&gt;Watch mode&lt;/a&gt; that's available in the UI and in the VS Code Extension.&lt;/p&gt;

&lt;p&gt;That's it! Let us know what you think and how you're using &lt;code&gt;--only-changed&lt;/code&gt;. Either in the comments here, in the &lt;a href="https://playwright.dev/community/welcome#community-discord" rel="noopener noreferrer"&gt;Community Discord&lt;/a&gt;, or on &lt;a href="https://playwright.dev/community/welcome#community-linkedin" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;! And have a wonderful day.&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>e2e</category>
      <category>testing</category>
    </item>
    <item>
      <title>Quirrel is acquired! And I am joining Netlify</title>
      <dc:creator>Simon Knott</dc:creator>
      <pubDate>Tue, 01 Feb 2022 15:59:26 +0000</pubDate>
      <link>https://dev.to/quirrel/quirrel-is-acquired-and-i-am-joining-netlify-dha</link>
      <guid>https://dev.to/quirrel/quirrel-is-acquired-and-i-am-joining-netlify-dha</guid>
      <description>&lt;p&gt;I am very happy to announce that &lt;a href="https://www.netlify.com/blog/quirrel-joins-netlify-and-scheduled-functions-launches-in-beta" rel="noopener noreferrer"&gt;Quirrel was acquired by Netlify&lt;/a&gt;, and I am joining as a software engineer. This is incredibly exciting for me, and I can’t wait to show you the fruits of this acquisition.&lt;/p&gt;

&lt;p&gt;Before I reflect on the journey, what this means for me, and how thankful I am for everything and everyone; let’s get the business things out of the way:&lt;/p&gt;

&lt;p&gt;What does this mean for current Quirrel users?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://quirrel.dev" rel="noopener noreferrer"&gt;quirrel.dev&lt;/a&gt;, the hosted Quirrel service, will continue operating through the end of July 2022. As of today, we will not be taking new sign-ups.&lt;/p&gt;

&lt;p&gt;Since Quirrel is (and will stay) open source, existing customers will be able to switch to self-hosted instances, and we (Netlify + me) will do the best we can to help make this transition process smooth for you.&lt;/p&gt;

&lt;p&gt;If you’re a customer, please reach out via &lt;a href="//mailto:info@quirrel.dev"&gt;info@quirrel.dev&lt;/a&gt; so we can coordinate on this.&lt;/p&gt;

&lt;p&gt;Taking the experience from building Quirrel, &lt;a href="https://ntl.fyi/sched-func" rel="noopener noreferrer"&gt;Netlify today launches &lt;em&gt;Scheduled Functions&lt;/em&gt; to Beta via Netlify Labs&lt;/a&gt;. It’s inspired by Quirrel’s &lt;code&gt;CronJob&lt;/code&gt; feature, deeply integrated within Netlify, and a joy to use. Please try it out and let me know what you think. :)&lt;/p&gt;

&lt;p&gt;If you have more questions about this, check out the &lt;a href="https://docs.quirrel.dev/netlify-acquisition-faq" rel="noopener noreferrer"&gt;FAQ&lt;/a&gt; and feel free to reach out .&lt;/p&gt;

&lt;p&gt;With that out of the way, let’s get to more details on this announcement!&lt;/p&gt;

&lt;p&gt;When I started working on Blitz.js in February 2020, I’d never have imagined how that journey would unfold.&lt;/p&gt;

&lt;p&gt;I got into web development in 2017, and got my feet wet in entrepreneurship by working on &lt;a href="https://ente.app" rel="noopener noreferrer"&gt;my first product&lt;/a&gt;. Economically speaking, it was a total failure (turns out, sales teams are important sometimes!). But it ignited my love for web development and - in particular - developer tooling, and led me to join &lt;a href="https://twitter.com/flybayer" rel="noopener noreferrer"&gt;Brandon Bayer&lt;/a&gt; + &lt;a href="https://github.com/blitz-js/blitz#contributors-" rel="noopener noreferrer"&gt;extended community’s&lt;/a&gt; quest to &lt;em&gt;build a better way for fullstack React applications,&lt;/em&gt; better known as &lt;a href="http://blitzjs.com" rel="noopener noreferrer"&gt;Blitz.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Eventually, every fullstack application grows to need some sort of a scheduling system. But there was no such thing for serverless deployments! I quickly realised this was a gap to fill. During a holiday in Croatia, an idea of how to build this crossed my mind. I shared an API mock-up on Twitter, and was surprised by how much it resonated. Not huge, but certainly bigger than expected. On the train back from that holiday, I started hacking together a first prototype. (Please don’t tell anybody it was faked and &lt;a href="https://github.com/quirrel-dev/demo.quirrel.dev/blob/ad3fdbdcc0180e02c0938ac9a0a27d80083a6b21/next-quirrel/repeater.ts#L4" rel="noopener noreferrer"&gt;used repeater.dev under the hood&lt;/a&gt;...). That’s how everything started.&lt;/p&gt;

&lt;p&gt;Fast-forward to now. You’ve read it in the title: Netlify brought me on to bring my experience with function scheduling into the Netlify product, and they acquired the (by now, significantly more mature) Quirrel project along the way. I’m proud to share the first fruits of that cooperation today. Netlify Scheduled Functions (also known as cron jobs, for you Linux peeps out there) goes to public beta, and, if people like it, there’s more to come.&lt;/p&gt;

&lt;p&gt;Being at Netlify now, I get to not only &lt;em&gt;build a better way for fullstack React&lt;/em&gt; but to &lt;em&gt;build a better web.&lt;/em&gt; To me, this is the most wonderful professional challenge, and I am super humbled to work alongside wonderful colleagues (thank you to the Netlify team including Eduardo and Ivan 💙).&lt;/p&gt;

&lt;p&gt;When I look back, I am more than happy to see how everything unfolded. I cannot thank anybody enough who’s been part of it, this journey would’ve been much different without you.&lt;/p&gt;

</description>
      <category>quirrel</category>
      <category>netlify</category>
      <category>jamstack</category>
    </item>
    <item>
      <title>Quirrel Newsletter #5 - Pricing RFC; Some Minor Updates</title>
      <dc:creator>Simon Knott</dc:creator>
      <pubDate>Tue, 26 Jan 2021 15:50:19 +0000</pubDate>
      <link>https://dev.to/quirrel/quirrel-newsletter-5-4jbf</link>
      <guid>https://dev.to/quirrel/quirrel-newsletter-5-4jbf</guid>
      <description>&lt;p&gt;It's the Quirrel Newsletter, this time with some minor updates.&lt;/p&gt;

&lt;p&gt;Before we get to those, I'd like to ask you a small favour: If you have a minute, please take a look at &lt;a href="https://simonknott.us2.list-manage.com/track/click?u=b9d062c0bb4e2206a305b8df7&amp;amp;id=8f2e50a3e6&amp;amp;e=7de658e3a3" rel="noopener noreferrer"&gt;this RFC&lt;/a&gt;. It's about changes to quirrel.dev pricing, and I'd love to get your feedback to make sure  they're in-line with community consent. (don't worry, it won't become more expensive ☺️)&lt;/p&gt;

&lt;p&gt;Alright, on to the new stuff:&lt;br&gt;
Quirrel now has a Blitz Recipe! &lt;code&gt;blitz install quirrel&lt;/code&gt; will set up everything you need to get started with it.&lt;br&gt;
Also, the Quirrel UI now allows you to store connection details as a bookmark, making it a lot more comfortable to use it for production monitoring.&lt;br&gt;
For some more minor changes, check out &lt;a href="https://simonknott.us2.list-manage.com/track/click?u=b9d062c0bb4e2206a305b8df7&amp;amp;id=9b96321a8b&amp;amp;e=7de658e3a3" rel="noopener noreferrer"&gt;the update thread&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That's it for this issue, thanks for reading! 🐿&lt;/p&gt;

&lt;p&gt;Best Wishes, Stay Safe&lt;br&gt;
Simon&lt;/p&gt;

</description>
      <category>quirrel</category>
    </item>
    <item>
      <title>Quirrel hits v1 🥳</title>
      <dc:creator>Simon Knott</dc:creator>
      <pubDate>Mon, 04 Jan 2021 13:51:52 +0000</pubDate>
      <link>https://dev.to/quirrel/quirrel-hits-v1-3odk</link>
      <guid>https://dev.to/quirrel/quirrel-hits-v1-3odk</guid>
      <description>&lt;p&gt;A happy new year everyone! 🎉&lt;/p&gt;

&lt;p&gt;I'm proud to announce: Quirrel goes to &lt;code&gt;v1&lt;/code&gt;.&lt;br&gt;
The release comes with improvements all over the board.&lt;br&gt;
Let's go through it!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; There's improved cron jobs, clients for 5 new frameworks and the Quirrel UI now also can be used for monitoring deployments. &lt;a href="https://docs.quirrel.dev/migrating-to-v1" rel="noopener noreferrer"&gt;Migrating to v1&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Improved Cron Jobs
&lt;/h2&gt;

&lt;p&gt;Cron Jobs have been a huge PITA:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I don't understand how and where I can register "good old cron jobs"?&lt;br&gt;
~ every Quirrel user, ever&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With &lt;code&gt;v1&lt;/code&gt;, they &lt;em&gt;finally&lt;/em&gt; get a well-deserved make-over:&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;// pages/api/myCronJobRoute.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;CronJob&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;quirrel/next&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nc"&gt;CronJob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;api/myCronJobRoute&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;5 4 * * *&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// every day at 04:05&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;// do something&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;That's it, that's everything you need to do.&lt;br&gt;
Your local Quirrel instance will automatically pick it up:&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%2Fi%2Frrpfzievr9vamx7kia88.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%2Fi%2Frrpfzievr9vamx7kia88.png" alt="Registered a Cron Job" width="468" height="118"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is implemented using filesystem watchers,&lt;br&gt;
similarly to how &lt;code&gt;tsc --watch&lt;/code&gt; works.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For Deployments, running &lt;code&gt;quirrel ci&lt;/code&gt; once during build will register all detected Cron Jobs with your production Quirrel instance.&lt;br&gt;
It will also remove obsolete ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Support for new Frameworks
&lt;/h2&gt;

&lt;p&gt;After seeing tremendous interest in Quirrel among Next.js and Blitz.js users,&lt;br&gt;
I'm adding support for 5 new frameworks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.quirrel.dev/api/next" rel="noopener noreferrer"&gt;Nuxt.js&lt;/a&gt; (&lt;code&gt;quirrel/nuxt&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.quirrel.dev/api/redwood" rel="noopener noreferrer"&gt;Redwood&lt;/a&gt; (&lt;code&gt;quirrel/redwood&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.quirrel.dev/api/vercel" rel="noopener noreferrer"&gt;Vercel Serverless Functions&lt;/a&gt; (&lt;code&gt;quirrel/vercel&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.quirrel.dev/api/express" rel="noopener noreferrer"&gt;Express.js&lt;/a&gt; (&lt;code&gt;quirrel/express&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.quirrel.dev/api/express" rel="noopener noreferrer"&gt;Connect&lt;/a&gt; (&lt;code&gt;quirrel/connect&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have another framework in mind that you'd like a Quirrel client for, let me know! :)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pre-v1, you had to install both &lt;code&gt;quirrel&lt;/code&gt; and &lt;code&gt;@quirrel/next&lt;/code&gt;.&lt;br&gt;
To ease maintenance, they were consolidated, so &lt;code&gt;@quirrel/next&lt;/code&gt; becomes &lt;code&gt;quirrel/next&lt;/code&gt; (notice the missing &lt;code&gt;@&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Until now, Next.js users could omit the leading &lt;code&gt;api/&lt;/code&gt; in their Queue's routes.&lt;br&gt;
Going forward, that won't be possible anymore, to match behaviour with the other framework client.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Quirrel UI for Production
&lt;/h2&gt;

&lt;p&gt;You've asked for it, so here it is: The &lt;a href="https://ui.quirrel.dev" rel="noopener noreferrer"&gt;Quirrel UI&lt;/a&gt; now supports connecting to non-local environments.&lt;br&gt;
The connection dialog can be opened using a new dropdown in the top left:&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%2Fi%2F8wfh6c75pxodpkvi1rip.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%2Fi%2F8wfh6c75pxodpkvi1rip.png" alt="Connection Dialog" width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It enables you to monitor &amp;amp; administrate any Quirrel deployment,&lt;br&gt;
which has been a heavily requested feature.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I'm thinking about adding a "Open in UI" button to the Quirrel Dashboard.&lt;br&gt;
Let me know if that'd be helpful for you.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why &lt;em&gt;this&lt;/em&gt; release was chosen for v1
&lt;/h2&gt;

&lt;p&gt;The previous state of Cron Jobs really nagged on me.&lt;br&gt;
Although they played an integral part to my initial vision for Quirrel,&lt;br&gt;
I struggled to find an easy-to-use design for a long time.&lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;br&gt;
Over the course of mutliple brainstorming sessions with some of you (thank you ❤️), we managed to find a good solution.&lt;br&gt;
With those improved Cron Jobs shipping in &lt;em&gt;v1&lt;/em&gt;, I'm &lt;em&gt;finally&lt;/em&gt; content with Quirrel's feature set.&lt;/p&gt;

&lt;p&gt;Moving to &lt;em&gt;v1&lt;/em&gt; also means "going stable".&lt;br&gt;
This is a big step, because we have to maintain that API for the foreseeable future.&lt;br&gt;
Judging by the feedback I received, the current API is well-designed and fit for its task,&lt;br&gt;
giving me the confidence to commit to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thank you.
&lt;/h2&gt;

&lt;p&gt;Since Quirrel's inception in Q3 last year, I've received amazing support from all of you. Thank you ❤️&lt;br&gt;
If you've put in the time to brainstorm with me: Thank you x2 ❤️ Your input has been incredibly valuable for Quirrel.&lt;/p&gt;

&lt;p&gt;As you know, I want to create the best developer experience possible.&lt;br&gt;
If there's anything that detracts from it, please let me know! :D&lt;/p&gt;

&lt;p&gt;To migrate your application to &lt;code&gt;v1&lt;/code&gt;, follow this Guide: &lt;a href="https://docs.quirrel.dev/migrating-to-v1" rel="noopener noreferrer"&gt;Migrating to v1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm looking forward to your feedback!&lt;/p&gt;

&lt;p&gt;All the best for 2021,&lt;br&gt;
Simon&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;CronJobs come with their special set of problems, primarily caused by serverless environments running forever. HMU if you want to read/hear more about that :D ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>quirrel</category>
      <category>jamstack</category>
      <category>happynewyear</category>
    </item>
    <item>
      <title>Quirrel Newsletter #3 - Incident Dashboards, Cron RFC</title>
      <dc:creator>Simon Knott</dc:creator>
      <pubDate>Thu, 10 Dec 2020 09:40:21 +0000</pubDate>
      <link>https://dev.to/quirrel/quirrel-newsletter-3-incident-dashboards-cron-rfc-2hm2</link>
      <guid>https://dev.to/quirrel/quirrel-newsletter-3-incident-dashboards-cron-rfc-2hm2</guid>
      <description>&lt;p&gt;Welcome to the third episode of the Quirrel Newsletter.&lt;/p&gt;

&lt;p&gt;This one is a quick one, I just want to let you know about a new feature: The &lt;em&gt;incident dashboard&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"incident"&lt;/em&gt; is what Quirrel calls an error in your application code.&lt;br&gt;
Until now, Quirrel didn't do anything when it encountered one - not exactly optimal, eh?&lt;br&gt;
Fear no longer! Quirrel will now send you an e-mail whenever one of your job handlers fails:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WNbekpif--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://pbs.twimg.com/media/EonwhvgXIAEDuWp%3Fformat%3Djpg%26name%3D4096x4096" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WNbekpif--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://pbs.twimg.com/media/EonwhvgXIAEDuWp%3Fformat%3Djpg%26name%3D4096x4096" alt="Incident E-Mails" width="800" height="561"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll then be able to see the incident's full job payload in the &lt;em&gt;incident dashboard&lt;/em&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%2Fi%2F0er3sagt7dh88aps3gqs.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%2Fi%2F0er3sagt7dh88aps3gqs.png" alt="Incident Dashboard" width="800" height="668"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since Quirrel jobs are end-to-end-encrypted, the dashboard also allows you to decrypt your job's payloads on the client-side :)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Quirrel users with on-premise hosted instances can specify how they want to receive incidents using the &lt;code&gt;INCIDENT_RECEIVER_ENDPOINT&lt;/code&gt; environment variable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But the incident dashboard is not the only thing that's changed.&lt;br&gt;
I'm currently preparing a big improvement to Cron jobs and published an RFC:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/quirrel-dev/quirrel/issues/31" rel="noopener noreferrer"&gt;https://github.com/quirrel-dev/quirrel/issues/31&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you've got a spare minute (and you most certainly have, if you're reading this), I'd love to get some feedback on that.&lt;/p&gt;

&lt;p&gt;Alright, that's it for this episode - a quick one, as promised!&lt;/p&gt;

&lt;p&gt;Thanks to all the nice people who helped bring these changes into existence, especially &lt;a href="https://twitter.com/maxfinke" rel="noopener noreferrer"&gt;Max&lt;/a&gt;! Really appreciate your support :D &lt;/p&gt;

&lt;p&gt;Best&lt;br&gt;
Simon&lt;/p&gt;

</description>
      <category>vercel</category>
      <category>nextjs</category>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>Quirrel Newsletter #2 - v0.10 🎉</title>
      <dc:creator>Simon Knott</dc:creator>
      <pubDate>Mon, 16 Nov 2020 15:31:38 +0000</pubDate>
      <link>https://dev.to/quirrel/quirrel-newsletter-2-v0-10-ld8</link>
      <guid>https://dev.to/quirrel/quirrel-newsletter-2-v0-10-ld8</guid>
      <description>&lt;p&gt;Hi there!&lt;/p&gt;

&lt;p&gt;Two weeks ago, I published you the first issue of this Newsletter.&lt;br&gt;
Today, let me tell you about the new features in &lt;a href="https://github.com/quirrel-dev/quirrel/releases/tag/v0.9.0" rel="noopener noreferrer"&gt;v0.9&lt;/a&gt; and &lt;a href="https://github.com/quirrel-dev/quirrel/releases/tag/v0.10.0" rel="noopener noreferrer"&gt;v0.10&lt;/a&gt;, which was released a couple of minutes ago.&lt;/p&gt;

&lt;p&gt;But before we do that: Let me invite you to our new Quirrel community over on Spectrum!&lt;br&gt;
It's still a little quiet over there, so come by and chat with us:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://spectrum.chat/quirrel" rel="noopener noreferrer"&gt;https://spectrum.chat/quirrel&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Alright, on to the new features.
&lt;/h2&gt;

&lt;p&gt;Quirrel now has human-friendly CLI output, which makes it even more fun to use:&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%2Fi%2Fpdk2svtainmm3wjfltcz.jpeg" 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%2Fi%2Fpdk2svtainmm3wjfltcz.jpeg" alt="Emm49m7W4AAD19E" width="680" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, the Quirrel UI now shows all running CRON jobs.&lt;/p&gt;

&lt;p&gt;On the API side, there are two new options for jobs:&lt;/p&gt;

&lt;p&gt;Marking a job as &lt;code&gt;exclusive&lt;/code&gt; guarantees it to be executed on its own, with no other jobs from the same queue running at the same time.&lt;br&gt;
This can be useful any time you want to prevent race conditions from happening, e.g. when implementing an order queue.&lt;/p&gt;

&lt;p&gt;When creating a job, you can now mark it with &lt;code&gt;override: true&lt;/code&gt;. Now when a job with the same ID already exists, the it is overriden by your job. This is a way to easily specify behaviour for use cases where idempotency is key.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other stuff that happened
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;an awesome call with Rishi from London, who requested the "exclusive jobs" feature (thanks, Rishi! great call :D)&lt;/li&gt;
&lt;li&gt;a very interesting &lt;a href="https://twitter.com/wesbos/status/1326939452107796482" rel="noopener noreferrer"&gt;conversation with Wes Bos&lt;/a&gt; about pricing models ...&lt;/li&gt;
&lt;li&gt;... which has led me to start working on the new pay-per-use pricing model&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Chatting with Quirrel users has proven itself as an incredibly effective way to get good feedback.&lt;br&gt;
If you can make time for it, I'd love to talk to you as well! Just drop me a DM &lt;a href="https://twitter.com/skn0tt" rel="noopener noreferrer"&gt;on Twitter&lt;/a&gt; :D&lt;/p&gt;

&lt;p&gt;That's been it for the second edition of the Quirrel Newsletter. Have a great week!&lt;/p&gt;

&lt;p&gt;Best&lt;br&gt;
Simon&lt;/p&gt;

</description>
      <category>vercel</category>
      <category>nextjs</category>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>Quirrel Newsletter #1 - Nibble Ahead</title>
      <dc:creator>Simon Knott</dc:creator>
      <pubDate>Mon, 02 Nov 2020 17:10:47 +0000</pubDate>
      <link>https://dev.to/quirrel/quirrel-newsletter-1-nibble-ahead-5a25</link>
      <guid>https://dev.to/quirrel/quirrel-newsletter-1-nibble-ahead-5a25</guid>
      <description>&lt;p&gt;Hello everyone, Simon from Quirrel here!&lt;/p&gt;

&lt;p&gt;Its my pleasure to present you the first issue of the Quirrel Newsletter.&lt;br&gt;
The Newsletter won't be published on a strict schedule, but rather as I see fit.&lt;br&gt;
It covers all things Quirrel: Improvements to the tool itself, interesting stories from Quirrel users, insights into the business of a bootstrapped MicroSaaS and lots more.&lt;/p&gt;

&lt;p&gt;Let's get into it, shall we? :D&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Quirrel, if you haven't heard about it, is a job queueing solution for serverless deployments, developed by &lt;a href="https://twitter.com/skn0tt" rel="noopener noreferrer"&gt;me&lt;/a&gt;. It's MIT licensed and I work in public, so &lt;a href="https://quirrel.dev" rel="noopener noreferrer"&gt;check it out&lt;/a&gt; :D&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  What's happened in the last weeks
&lt;/h1&gt;

&lt;p&gt;In true changelog manner, let me start by giving you an overview of the developments of the last weeks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quirrel now supports &lt;a href="https://docs.quirrel.dev/api-reference/next#cron-job" rel="noopener noreferrer"&gt;cron jobs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;the development environment is now published &lt;a href="https://www.npmjs.com/package/quirrel" rel="noopener noreferrer"&gt;to NPM&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;the onboarding experience has been improved&lt;/li&gt;
&lt;li&gt;the client now features JSDoc comments for improved DX&lt;/li&gt;
&lt;li&gt;you can now see the status of Quirrel infrastructure on the  &lt;a href="https://status.quirrel.dev" rel="noopener noreferrer"&gt;status page&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;delays can now be specified in a human-friendly way using &lt;a href="https://github.com/vercel/ms" rel="noopener noreferrer"&gt;&lt;code&gt;vercel/ms&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;I've added Telemetry&lt;/li&gt;
&lt;li&gt;a &lt;a href="https://dev.to/quirrel/building-a-water-drinking-reminder-with-next-js-and-quirrel-1ckj"&gt;new Tutorial&lt;/a&gt; has been published&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see, a lot of work has been done, making Quirrel even more useful.&lt;/p&gt;

&lt;p&gt;One of the more future-facing efforts I started is called &lt;a href="https://github.com/quirrel-dev/owl" rel="noopener noreferrer"&gt;"Owl"&lt;/a&gt;.&lt;br&gt;
Owl is a queuing library tailor-made for Quirrel, aimed at replacing &lt;a href="https://github.com/taskforcesh/bullmq" rel="noopener noreferrer"&gt;bullmq&lt;/a&gt;.&lt;br&gt;
It will make Quirrel more performant and remove the need to run Redis during development (that means no more Docker for you Next.js developers 🎉).&lt;/p&gt;

&lt;p&gt;The past weeks have also been accompanied by a lot of wonderful interactions with Quirrel users.&lt;br&gt;
I don't have space to list all of them, but I'd especially like to thank &lt;a href="https://twitter.com/maxfinke" rel="noopener noreferrer"&gt;Max Finke&lt;/a&gt;, &lt;a href="https://twitter.com/janbhwilhelm" rel="noopener noreferrer"&gt;Jan Wilhelm&lt;/a&gt; and &lt;a href="https://twitter.com/timsuchanek" rel="noopener noreferrer"&gt;Tim Suchanek&lt;/a&gt; for brainstorming with me.&lt;/p&gt;

&lt;p&gt;From a business perspective, it's going well.&lt;br&gt;
&lt;a href="https://quirrel.dev" rel="noopener noreferrer"&gt;quirrel.dev&lt;/a&gt; (the hosted Quirrel instance) currently has 53 registered users across four continents, and a handful of them have even deployed an application using it.&lt;br&gt;
There's one paying customer, putting Quirrel at a MRR of 10$.&lt;br&gt;
Not much, I know - but considering Quirrel is a 2-months-old product that's in full development, it's quite a good start.&lt;/p&gt;

&lt;h1&gt;
  
  
  The State of Quirrel
&lt;/h1&gt;

&lt;p&gt;"Where are we, in the Quirrel journey?"&lt;/p&gt;

&lt;p&gt;The answer to that question determines what I should focus on:&lt;br&gt;
If the product is &lt;em&gt;done&lt;/em&gt;, I should focus on promoting it and growing its userbase.&lt;br&gt;
If it's &lt;em&gt;in development&lt;/em&gt; or &lt;em&gt;emerging&lt;/em&gt;, doing the opposite would be wise: Collaborate with existing users to improve the product, but don't expand yet (to prevent churn).&lt;/p&gt;

&lt;p&gt;At the moment, Quirrel is definitely &lt;em&gt;in development&lt;/em&gt;.&lt;br&gt;
While I'm kind of satisfied with its features, I still need to find out more about the way people use Quirrel and their individual needs.&lt;/p&gt;

&lt;p&gt;So if you use Quirrel, I'd love to hear from you! Drop a comment or leave me a DM &lt;a href="https://twitter.com/skn0tt" rel="noopener noreferrer"&gt;on Twitter&lt;/a&gt;, I won't bite 😇&lt;/p&gt;

&lt;p&gt;Once I'm confident that I achieved "product market fit", I'll ramp up the Quirrel media presence and bring more users onboard.&lt;/p&gt;

&lt;h1&gt;
  
  
  Outlook
&lt;/h1&gt;

&lt;p&gt;So now that we know about the Status Quo, let's take a look ahead.&lt;/p&gt;

&lt;p&gt;There's a couple of upcoming changes I'm planning to make:&lt;/p&gt;

&lt;p&gt;By the end of the week, Quirrel will transition to &lt;code&gt;Owl&lt;/code&gt;. This will enable Quirrel users to run the local development version of Quirrel without having to run Redis.&lt;/p&gt;

&lt;p&gt;I'm also experimenting with &lt;a href="https://simonknott.de/articles/Fire-and-forget-HTTP-Requests.html" rel="noopener noreferrer"&gt;Fire-and-forget HTTP requests&lt;/a&gt;, which has the potential to substantially improve throughput on large Quirrel instances.&lt;br&gt;
This change will be made once the performance improvements become necessary.&lt;/p&gt;

&lt;p&gt;Quirrel.dev will also see a slight change to its pricing model. The current 20$/month plan costs the same, wether you make 500 or 10.000 calls.&lt;br&gt;
This strongly favours high-usage projects for no apparent reason.&lt;br&gt;
I'm currently thinking about introducing a pay-per-use model with a lower base fee and additional charges based on usage, which is a model that multiple users have already suggested.&lt;/p&gt;

&lt;p&gt;Last, but not least, let's talk about client libraries.&lt;br&gt;
While at the moment, I'm talking about Quirrel as the job queueing solution for &lt;em&gt;Next.js&lt;/em&gt;, it will be equally useful for other frameworks in the future.&lt;br&gt;
I'm currently planning on creating integrations for Redwood, Sapper, Nuxt and Express. Which framework would you like to see a client library for?&lt;/p&gt;

&lt;h1&gt;
  
  
  Thanks for reading!
&lt;/h1&gt;

&lt;p&gt;If you've made it this far, that shows quite some dedication.&lt;/p&gt;

&lt;p&gt;I'd like to emphasise on what I wrote earlier:&lt;/p&gt;

&lt;p&gt;If you use Quirrel (and even if you don't), I'd love to hear from you! Drop a comment or leave me a DM &lt;a href="https://twitter.com/skn0tt" rel="noopener noreferrer"&gt;on Twitter&lt;/a&gt;, I won't bite 😇&lt;/p&gt;

&lt;p&gt;Anyway, I hope this Newsletter was interesting to you. What new stuff did you learn about Quirrel? Let me know :D&lt;/p&gt;

&lt;p&gt;Have a great week,&lt;/p&gt;

&lt;p&gt;Simon&lt;/p&gt;

</description>
      <category>vercel</category>
      <category>nextjs</category>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>Building a water-drinking reminder with Next.js and Quirrel</title>
      <dc:creator>Simon Knott</dc:creator>
      <pubDate>Thu, 15 Oct 2020 08:34:03 +0000</pubDate>
      <link>https://dev.to/quirrel/building-a-water-drinking-reminder-with-next-js-and-quirrel-1ckj</link>
      <guid>https://dev.to/quirrel/building-a-water-drinking-reminder-with-next-js-and-quirrel-1ckj</guid>
      <description>&lt;p&gt;Hi there! Welcome to the first ever post on the Quirrel blog!&lt;br&gt;
Quirrel is an open-source task queueing service that's easy to use and sparks joy.&lt;br&gt;
In this tutorial, we're going to use it to create a water drinking reminder - because if you're anything like me, it's easy to forget sometimes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Check out the finished version here: &lt;a href="https://water-reminder.quirrel.dev" rel="noopener noreferrer"&gt;water-reminder.quirrel.dev&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;To follow this tutorial, you should already know a little bit about Next.js.&lt;br&gt;
If you’re totally new to it, check out the &lt;a href="https://nextjs.org/learn" rel="noopener noreferrer"&gt;Next tutorial&lt;/a&gt; first.&lt;/p&gt;




&lt;h1&gt;
  
  
  Let's get started!
&lt;/h1&gt;

&lt;p&gt;The first thing we're gonna do is to create a new Next.js project.&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;$ &lt;/span&gt;npx create-next-app water-reminder


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

&lt;/div&gt;

&lt;p&gt;Open up &lt;code&gt;water-reminder&lt;/code&gt; in your favourite editor and run &lt;code&gt;npm run dev&lt;/code&gt; to startup the development environment.&lt;/p&gt;

&lt;p&gt;Take a look into &lt;code&gt;pages/index.js&lt;/code&gt; and replace its content with the following:&lt;/p&gt;

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

&lt;span class="c1"&gt;// pages/index.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Water Drinking Reminder
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        I want to be reminded under the following e-mail:
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;
        &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formData&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;FormData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nf"&gt;alert&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="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;
          &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"E-Mail"&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          Submit
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;It contains some markup and a simple form that lets you submit the e-mail you want to be reminded at.&lt;br&gt;
At the moment, it will just alert the typed email.&lt;br&gt;
In your browser, open up &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;localhost:3000&lt;/a&gt;.&lt;br&gt;
It should look similar to this:&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%2Fi%2Fyv3au7nf6yv019tlqrqo.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%2Fi%2Fyv3au7nf6yv019tlqrqo.png" alt="Submission Form"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Submitting the form to the backend
&lt;/h2&gt;

&lt;p&gt;Setup a new API Route by creating &lt;code&gt;pages/api/setupReminder.js&lt;/code&gt; and adding the following code:&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;// pages/api/setupReminder.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&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;`I'll setup the reminder for &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="s2"&gt;.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="nf"&gt;end&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;Now instead of &lt;code&gt;alert&lt;/code&gt;-ing the form value, let's post it to the newly created API route.&lt;br&gt;
Go back to &lt;code&gt;pages/index.js&lt;/code&gt; and replace the following:&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;// pages/index.js&lt;/span&gt;
&lt;span class="nx"&gt;onSubmit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formData&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;FormData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&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="nf"&gt;alert&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="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/setupReminder&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&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="p"&gt;}}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Submitting the form will now cause the e-mail to be printed to your development console:&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%2Fi%2F2vobnhgwzxymupy2zw8c.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%2Fi%2F2vobnhgwzxymupy2zw8c.png" alt="Screenshot 2020-10-13 at 16.19.35"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we've hooked up the form with the API Route, let's get into Quirrel and E-Mail sending.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up Quirrel
&lt;/h2&gt;

&lt;p&gt;Quirrel is a task queueing service.&lt;br&gt;
It takes requests à la "call me back at /api/queue in 10 minutes", stores them and makes sure to call back &lt;code&gt;/api/queue&lt;/code&gt; as requested.&lt;br&gt;
What's awesome about Quirrel is that it can run fully locally using the Quirrel CLI:&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;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; quirrel
&lt;span class="nv"&gt;$ &lt;/span&gt;quirrel
🎉 Starting the Quirrel Dev Environment 🎉


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

&lt;/div&gt;

&lt;p&gt;Now create a new queue by creating &lt;code&gt;pages/api/queues/reminder.js&lt;/code&gt; and typing the following:&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;// pages/api/queues/reminder.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;Queue&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;quirrel/next&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nc"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;api/queues/reminder&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// the route it's reachable under&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;recipient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Sending an E-Mail to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;recipient&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;&lt;code&gt;Queue&lt;/code&gt; takes two arguments: The first one is it's API route location, and the second one is the worker function.&lt;br&gt;
Whenever a job is executed, the worker function is called.&lt;/p&gt;

&lt;p&gt;To use our newly created Queue, simply import it from the API Route:&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;// pages/api/setupReminder.js&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;reminderQueue&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./queues/reminder&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// 👆 don't forget this&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&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;`I'll setup the reminder for &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="s2"&gt;.`&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;reminderQueue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;   &lt;span class="nx"&gt;email&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="o"&gt;+&lt;/span&gt;     &lt;span class="na"&gt;id&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="o"&gt;+&lt;/span&gt;     &lt;span class="na"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;30min&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="na"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;       &lt;span class="na"&gt;every&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;30min&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="na"&gt;times&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 16 * 30min = 8h&lt;/span&gt;
&lt;span class="o"&gt;+&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="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="nf"&gt;end&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;Calling &lt;code&gt;.enqueue&lt;/code&gt; will schedule a new job.&lt;br&gt;
The first argument is the job's payload while the second argument contains some options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;id&lt;/code&gt; prevents having multiple reminders for the same e-mail address&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;repeat&lt;/code&gt; makes the job execute on twice an hour, for a duration of 8 hours&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;delay&lt;/code&gt; adds an initial delay of 30 minutes, so the first job isn't executed immediately&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To verify that this works, open up the Quirrel Development UI at &lt;a href="https://ui.quirrel.dev/pending" rel="noopener noreferrer"&gt;ui.quirrel.dev&lt;/a&gt;.&lt;br&gt;
It will connect to your local Quirrel instance and show all pending jobs in the "Pending" tab:&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%2Fi%2Fr8ekoytptmjnli7gcssn.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%2Fi%2Fr8ekoytptmjnli7gcssn.png" alt="Quirrel UI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If it doesn't connect, that may be because you're using Safari. Try a different browser instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Submitting your email to the form at &lt;code&gt;localhost:3000&lt;/code&gt; will add a new job to the UI, and pressing "Invoke" will execute the job.&lt;br&gt;
You'll now be able to see &lt;code&gt;Sending an E-Mail to XYZ&lt;/code&gt; in your development logs.&lt;/p&gt;

&lt;p&gt;Because it's a repeated job, it will be re-scheduled immediately, until it's been executed for the 16th time.&lt;/p&gt;

&lt;p&gt;Before we proceed with the last part of the tutorial: Stand up, go to the kitchen and grab a glass of water 💧&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's hook up E-Mail!
&lt;/h2&gt;

&lt;p&gt;Now that the Queue is working, let's hook up the final thing: E-Mail!&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;npm install nodemailer&lt;/code&gt; and add your SMTP setup code to your reminder queue:&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;// pages/api/queues/reminder.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;Queue&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;quirrel/next&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createTransport&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nodemailer&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createTransport&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;   &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;smtp.ethereal.email&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="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;587&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;   &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;     &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;randall.renner66@ethereal.email&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="na"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Dp5pzSVa52BJwypJQm&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="p"&gt;},&lt;/span&gt;
&lt;span class="o"&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;If you don't have any SMTP credentials at hand, you can get some demo ones &lt;a href="https://ethereal.email/create" rel="noopener noreferrer"&gt;at ethereal.email&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then simply switch out the &lt;code&gt;console.log&lt;/code&gt; call with a real email dispatch:&lt;/p&gt;

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

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nc"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;api/queues/reminder&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;recipient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;-&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;`Sending an E-Mail to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;recipient&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="o"&gt;+&lt;/span&gt;   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendMail&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;     &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;     &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;waterreminder@quirrel.dev&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="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Remember to drink some water!&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="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="o"&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;That's it! Now our app is fully working.&lt;br&gt;
It may not be the best water reminder service ever, but it's your very own one.&lt;/p&gt;

&lt;p&gt;Here are some ideas to improve it further:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make duration and interval of the reminder configurable&lt;/li&gt;
&lt;li&gt;Allow users to unsubscribe using a link in the email&lt;/li&gt;
&lt;li&gt;Add some styles, maybe using &lt;a href="https://tailwindcss.com" rel="noopener noreferrer"&gt;Tailwind CSS&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Deploying this to production is easy using the &lt;a href="https://quirrel.dev" rel="noopener noreferrer"&gt;managed Quirrel service&lt;/a&gt;.&lt;br&gt;
Simply follow this guide: &lt;a href="https://docs.quirrel.dev/deployments/vercel" rel="noopener noreferrer"&gt;Deploying Quirrel to Vercel&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;We've built a working water reminder in little under an hour.&lt;br&gt;
You can see the finished project here: &lt;a href="https://github.com/quirrel-dev/water-reminder-demo" rel="noopener noreferrer"&gt;quirrel-dev/water-reminder-demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you've got experience with well-known task queueing libraries like beanstalkd or SideKiq, you may have noticed how easy-to-use Quirrel is.&lt;br&gt;
The highly integrated client libraries for popular frameworks and the managed solution available, Quirrel is a great choice for JAMStack users.&lt;/p&gt;

&lt;p&gt;And if you want to host Quirrel yourself, the MIT License allows you to do so.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>typescript</category>
      <category>vercel</category>
    </item>
    <item>
      <title>Fire-and-forget HTTP Requests using Node.js</title>
      <dc:creator>Simon Knott</dc:creator>
      <pubDate>Wed, 14 Oct 2020 09:00:00 +0000</pubDate>
      <link>https://dev.to/skn0tt/fire-and-forget-http-requests-using-node-js-l1p</link>
      <guid>https://dev.to/skn0tt/fire-and-forget-http-requests-using-node-js-l1p</guid>
      <description>&lt;p&gt;In most cases, we care about the results of our HTTP Requests. But sometimes, it’s just about &lt;em&gt;making&lt;/em&gt; the request, and not so much about the response. In these cases, it may be wise to save yourself some network bandwidth by ignoring the response entirely.&lt;/p&gt;

&lt;h1&gt;
  
  
  How does this work?
&lt;/h1&gt;

&lt;p&gt;HTTP Requests are based on TCP connections. Running &lt;code&gt;curl google.com&lt;/code&gt; will open up a TCP connection to &lt;code&gt;google.com:80&lt;/code&gt; and send roughly the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;google.com&lt;/span&gt;
&lt;span class="na"&gt;User-Agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;curl/7.64.1&lt;/span&gt;
&lt;span class="na"&gt;Accept&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;*/*&lt;/span&gt;

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

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Line&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Request Line. Contains Method, Location and Protocol Version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;The only &lt;em&gt;required&lt;/em&gt; header: “Host”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3, 4&lt;/td&gt;
&lt;td&gt;Some Headers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;blank line to separate header and body&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Normally, you’ll then wait for the server to respond like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; 
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;text/html&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&lt;/span&gt; ...

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

&lt;/div&gt;



&lt;p&gt;But the response isn’t of interest to us. We’ll just send off our request, then terminate the TCP connection and carry on with more interesting stuff.&lt;/p&gt;

&lt;h1&gt;
  
  
  A rough test implementation
&lt;/h1&gt;

&lt;p&gt;The first thing we need is a test server we can make our calls against:&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;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;http&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="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;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Done&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Done&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="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;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To simulate time-consuming work, this server delays responding by one second.&lt;/p&gt;

&lt;p&gt;Now onto the interesting part: Making the manual HTTP request.&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;net&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;net&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&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;net&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// request line&lt;/span&gt;
  &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST / HTTP/1.1&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;// headers&lt;/span&gt;
  &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Host: localhost:5000&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="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Content-Length: 11\r\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// blank line&lt;/span&gt;
  &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&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;// body&lt;/span&gt;
  &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&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="c1"&gt;// end the connection prematurely&lt;/span&gt;
  &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;client&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="s2"&gt;close&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Connection Closed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s rather simple! We open up the connection to &lt;code&gt;localhost:5000&lt;/code&gt; and dispatch the request. The important line is the call to &lt;code&gt;client.destroy()&lt;/code&gt;, which will end the socket immediately after the request has been transmitted.&lt;/p&gt;

&lt;p&gt;Let’s see it in action:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nYyZ_r-I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://simonknott.de/assets/fire-and-forget/fire-and-forget.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nYyZ_r-I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://simonknott.de/assets/fire-and-forget/fire-and-forget.gif" alt="Fire and Forget in Action" width="600" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Fire-and-forget HTTP calls aren’t that hard to make. They can be of use in bandwith-constrained environments - I will probably make use of them for &lt;a href="https://github.com/quirrel.dev" rel="noopener noreferrer"&gt;Quirrel&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If this post was useful or interesting to you, make sure leave a comment or write me a message on Twitter! I always love to hear about my content helping others.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>network</category>
      <category>quirrel</category>
    </item>
    <item>
      <title>SuperJSON - JSON on steroids</title>
      <dc:creator>Simon Knott</dc:creator>
      <pubDate>Sun, 04 Oct 2020 22:00:00 +0000</pubDate>
      <link>https://dev.to/skn0tt/superjson-json-on-steroids-4l9b</link>
      <guid>https://dev.to/skn0tt/superjson-json-on-steroids-4l9b</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/blitz-js/superjson" rel="noopener noreferrer"&gt;SuperJSON&lt;/a&gt; is a high-fidelity replacement to &lt;code&gt;JSON.stringify&lt;/code&gt;. It supports data types like &lt;code&gt;Date&lt;/code&gt;, &lt;code&gt;RegExp&lt;/code&gt;, &lt;code&gt;Map&lt;/code&gt;, &lt;code&gt;Set&lt;/code&gt; etc., preserves referential equality and supports circular references. This blog post aims to compare it to alternatives and explain how it works.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s the Problem?
&lt;/h3&gt;

&lt;p&gt;JSON is the defacto standard data interchange format for web applications. It’s human-readable, has broad tooling support and its choice on data types are compatible with most platforms.&lt;/p&gt;

&lt;p&gt;As usage of JavaScript across backend and frontend became more popular, one of its value propositions have been let down by JSON: It doesn’t support data types like &lt;code&gt;Date&lt;/code&gt;, &lt;code&gt;RegExp&lt;/code&gt;, &lt;code&gt;Map&lt;/code&gt; or &lt;code&gt;Set&lt;/code&gt;, forcing developers to write cumbersome adapter logic to work around these limitations.&lt;/p&gt;

&lt;p&gt;This problem became especially apparent while working on &lt;a href="https://blitzjs.com" rel="noopener noreferrer"&gt;Blitz.js&lt;/a&gt;. Blitz.js is a fullstack React framework that (amongst other things) generates your applications’ API layer at compile time. It thus allows developers to import backend functions straight from the frontend, completely abstracting away network calls. Here’s the problem: If we used &lt;code&gt;JSON.stringify&lt;/code&gt;, our developers would be surprised because it destroyed their nice data types! That’s where SuperJSON comes into play: It’s able to serialize any JavaScript value and thus saves our developers from quite some headache.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other ways to solve this
&lt;/h2&gt;

&lt;p&gt;SuperJSON is not the first contender, other approaches that solve this problem have existed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Rich-Harris/devalue" rel="noopener noreferrer"&gt;devalue&lt;/a&gt; by Rich Harris is an algorithm that encodes value into JavaScript code that creates it - it turns &lt;code&gt;{ foo: "bar", self: this }&lt;/code&gt; into &lt;code&gt;(function(a){a.foo="bar";a.self=a;return a}({}))&lt;/code&gt;, which can then be evaluated to get back the original value. While it’s incredibly efficient&lt;sup id="fnref:benchmark"&gt;1&lt;/sup&gt;, there isn’t any tooling support for it, and it also can’t be used to send data to the server - what if a malicious client sent &lt;code&gt;while (true) { /* do nothing */ }&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;A similar approach is taken by Yahoo’s &lt;a href="https://github.com/yahoo/serialize-javascript" rel="noopener noreferrer"&gt;Serialize JavaScript&lt;/a&gt;, but it suffers from the same problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Goals of SuperJSON
&lt;/h2&gt;

&lt;p&gt;Contrary the approaches mentioned above, SuperJSON aims to …&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;… support any JavaScript value&lt;/li&gt;
&lt;li&gt;… be safe from remote code execution&lt;/li&gt;
&lt;li&gt;… be JSON-compatible, so existing tooling can be leveraged&lt;/li&gt;
&lt;li&gt;… support circular references&lt;/li&gt;
&lt;li&gt;… preserve referential equalities&lt;/li&gt;
&lt;li&gt;… be human-readable, so it’s easy to debug&lt;/li&gt;
&lt;li&gt;… be easy-to-understand, even if you don’t know it’s SuperJSON&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How does SuperJSON solve it?
&lt;/h2&gt;

&lt;p&gt;There are two parts to what we’re trying to achieve. We need to …&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;… transform any JavaScript value into a JSON-compatible one&lt;/li&gt;
&lt;li&gt;… be able to reverse it!&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Transforming into a JSON-compatible value
&lt;/h3&gt;

&lt;p&gt;Transforming is quite easy, actually: For already compatible ones, it’s trivial: &lt;code&gt;42&lt;/code&gt; becomes &lt;code&gt;42&lt;/code&gt;, &lt;code&gt;"lol"&lt;/code&gt; becomes &lt;code&gt;"lol"&lt;/code&gt;, &lt;code&gt;true&lt;/code&gt; becomes &lt;code&gt;true&lt;/code&gt; and &lt;code&gt;false&lt;/code&gt; becomes &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It’s a bit harder for values where there is no JSON counterpart: &lt;code&gt;Set { "foo", "bar" }&lt;/code&gt; becomes &lt;code&gt;["foo", "bar"]&lt;/code&gt;, &lt;code&gt;Map { 1 =&amp;gt; 11, 2 =&amp;gt; 22 }&lt;/code&gt; becomes &lt;code&gt;[[1, 11], [2, 22] ]&lt;/code&gt; and &lt;code&gt;NaN&lt;/code&gt; becomes &lt;code&gt;"NaN"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So given this value …&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    a: new Set([/a-Z/g]),
    b: new Map([
        [1, NaN],
        [2, NaN]
    ])
}

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

&lt;/div&gt;



&lt;p&gt;… we’ll create this json:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "a": ["/a-Z/g"],
    "b": [
        [1, "NaN"],
        [2, "NaN"]
    ]
}

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

&lt;/div&gt;



&lt;p&gt;Easy, right? This can done recursively and fits into a couple of lines of code.&lt;/p&gt;

&lt;p&gt;But we won’t be able to reverse this, right? Unless … we just take notes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "a": "set",
    "a.0": "RegExp",
    "b": "map",
    "b.0.1": "number",
    "b.1.1": "number"
}

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

&lt;/div&gt;



&lt;p&gt;This contains notes on any non-trivial transformations.&lt;sup id="fnref:notes-confession"&gt;2&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;We can then safely send both our JSON-ified value and the notes over the network. During deserialization, we then apply the notes to recreate the original value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;value["b"][1][1] = Number(value["b"][1][1])
value["b"][0][1] = Number(value["b"][0][1])
value["b"] = new Map(value["b"])
...

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

&lt;/div&gt;



&lt;p&gt;Not so hard, right? Now that we can preserve types, let’s see how to preserve referential equalitites.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preserving Referential Equalities
&lt;/h3&gt;

&lt;p&gt;Imagine the object that’s created by the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const a = { id: "a" }
const b = { id: "b" }

{ options: [a, b], selected: a }

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

&lt;/div&gt;



&lt;p&gt;As stated in the Goals, we want to preserve the fact that &lt;code&gt;options[0] === selected&lt;/code&gt;. This can be done by not only taking notes on types to recreate, but also on referential equality. For the value above, SuperJSON takes the following notes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "selected": ["options.0"]
}

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

&lt;/div&gt;



&lt;p&gt;We can then use these notes during deserialization like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;value["options"]["0"] = value["selected"]

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

&lt;/div&gt;



&lt;p&gt;This feature can also be used to preserve circular references.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go use it!
&lt;/h2&gt;

&lt;p&gt;SuperJSON is currently in beta, it should be ready for you to use in your own projects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --save superjson
yarn add superjson


import SuperJSON from "superjson"

const string = SuperJSON.stringify(yourValue)
const profit = SuperJSON.parse(string)

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

&lt;/div&gt;



&lt;p&gt;We’re currently testing support for preserving &lt;code&gt;class&lt;/code&gt; and &lt;code&gt;Symbol&lt;/code&gt; instances, we’ll also work on making SuperJSON faster and smaller.&lt;/p&gt;

&lt;p&gt;I’d love to hear what you think of it! Tweet at me: &lt;a href="https://twitter.com/skn0tt" rel="noopener noreferrer"&gt;@skn0tt&lt;/a&gt; or write a comment below.&lt;/p&gt;

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

&lt;p&gt;see &lt;a href="https://github.com/Rich-Harris/superjson-and-devalue" rel="noopener noreferrer"&gt;this benchmark&lt;/a&gt; for comparison ↩&lt;/p&gt;

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

&lt;p&gt;In reality, we’re encoding these in a tree structure to save space. But that’s a topic for another post 😄 ↩&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>algorithms</category>
    </item>
  </channel>
</rss>
