<?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: Stephan Meijer</title>
    <description>The latest articles on DEV Community by Stephan Meijer (@smeijer).</description>
    <link>https://dev.to/smeijer</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%2F163536%2F99565e6b-c04d-4165-985b-16990c8e1dd9.png</url>
      <title>DEV Community: Stephan Meijer</title>
      <link>https://dev.to/smeijer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/smeijer"/>
    <language>en</language>
    <item>
      <title>Linking Packages with Yalc</title>
      <dc:creator>Stephan Meijer</dc:creator>
      <pubDate>Sat, 08 Jul 2023 10:13:14 +0000</pubDate>
      <link>https://dev.to/smeijer/linking-packages-with-yalc-23j1</link>
      <guid>https://dev.to/smeijer/linking-packages-with-yalc-23j1</guid>
      <description>&lt;p&gt;As web developers, we often work on projects involving multiple packages or modules, that are under simultaneous development, making it necessary to link them together to test integration. The traditional approach for this uses &lt;code&gt;npm link&lt;/code&gt;, which creates a symbolic link between the package in development and the global &lt;code&gt;node_modules&lt;/code&gt; directory. The better way is to use &lt;code&gt;yalc&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://npmjs.com/yalc"&gt;Yalc&lt;/a&gt; is a simple package manager for local package development. It acts as a simple local repository for your packages that you can publish to and install from, just like you would with a remote package registry, such as npm or yarn. Yalc simplifies package linking by avoiding some of the complexities and pitfalls associated with &lt;code&gt;npm link&lt;/code&gt;, yet it's much easier to work with than running a local registry like &lt;a href="https://npmjs.com/verdaccio"&gt;verdaccio&lt;/a&gt;. Yalc is a set of scripts, not a running service.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why use Yalc over npm link?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;npm link&lt;/code&gt; creates a symbolic link for your local package in the global &lt;code&gt;node_modules&lt;/code&gt; directory. This can lead to confusion as packages can unknowingly share dependencies, causing unintended side effects. If you've ever tried to link a package that depends on react, the "&lt;a href="https://legacy.reactjs.org/warnings/invalid-hook-call-warning.html"&gt;invalid hook call&lt;/a&gt;" error might sound familiar.&lt;/p&gt;

&lt;p&gt;Yalc operates more like a local publishing system. Instead of linking, it pushes the package content into the consumer project, mimicking the process of publishing and installing a package. This results in an experience closer to working with an actual dependency, minimizing environment inconsistencies and unexpected behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to use Yalc?
&lt;/h3&gt;

&lt;p&gt;Using Yalc is straightforward. First, you need to install it globally on your machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; yalc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After it's installed, navigate to the directory of the package. Then make sure to build the package and run &lt;code&gt;yalc publish&lt;/code&gt;. Yalc only copies the files over that should be published to NPM, so building is essential.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build
yalc publish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running that command, your package has been "published" to the global Yalc store at &lt;code&gt;~/.yalc/packages&lt;/code&gt;. Then, in your consumer project, you can install it as you would with a regular npm package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yalc add &amp;lt;your-package-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That does a couple of things. It updates the &lt;code&gt;yalc.lock&lt;/code&gt; at your project, copies the package over from the global store, to the local one at &lt;code&gt;.yalc&lt;/code&gt;, and modifies &lt;code&gt;package.json&lt;/code&gt; to add the dependency or update it to point to the files in the &lt;code&gt;.yalc/&amp;lt;your-package-name&amp;gt;&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;Your package is now added to your project as if it were a regular dependency. When you change your package and want to update it in your project, you can rebuild it, and use &lt;code&gt;yalc push&lt;/code&gt; to push an update. &lt;code&gt;yalc push&lt;/code&gt; will publish the package to the global store, and push the changes to all linked projects local stores in one command.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;For the best experience, you want to hook up &lt;code&gt;yalc push&lt;/code&gt; to your &lt;code&gt;build --watch&lt;/code&gt; command, so that it automatically pushes changes to your linked projects. But manual pushing goes a long way too.&lt;/p&gt;

&lt;h3&gt;
  
  
  Retreat and Restore
&lt;/h3&gt;

&lt;p&gt;As I mentioned earlier; yalc doesn't create symlinks, it copies the publishable assets of the package into your project. Your project will get a &lt;code&gt;.yalc&lt;/code&gt; directory, containing the package, and a &lt;code&gt;yalc.lock&lt;/code&gt; file. Both can be committed to your repository, but if you should or not is up to you. I don't, but I can imagine some use cases where you might need to.&lt;/p&gt;

&lt;p&gt;If you don't commit them you'll have to run &lt;code&gt;yalc retreat --all&lt;/code&gt; before you commit. That command removes all yalc references from the package.json. Complementary, &lt;code&gt;yalc restore&lt;/code&gt; reads your &lt;code&gt;yalc.lock&lt;/code&gt; and restores any missing links.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final words
&lt;/h3&gt;

&lt;p&gt;While &lt;code&gt;npm link&lt;/code&gt; offers a quick way to link local packages, Yalc provides a more robust and predictable workflow that mimics using a real dependency. Using Yalc, you can avoid some of the pitfalls of &lt;code&gt;npm link&lt;/code&gt; and make your local package development more efficient and reliable.&lt;/p&gt;




&lt;p&gt;Liked this article? Share your thoughts on &lt;a href="https://twitter.com/intent/tweet?url=https://meijer.ws/articles/linking-packages-with-yalc"&gt;Twitter&lt;/a&gt;, and check my other &lt;a href="https://meijer.ws/articles"&gt;articles&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Isomorphic Development</title>
      <dc:creator>Stephan Meijer</dc:creator>
      <pubDate>Sat, 08 Jul 2023 10:11:43 +0000</pubDate>
      <link>https://dev.to/smeijer/isomorphic-development-51de</link>
      <guid>https://dev.to/smeijer/isomorphic-development-51de</guid>
      <description>&lt;p&gt;At first, I wanted to name this post "No, Server Components Don't Make Your Job Easier", but that wouldn't have covered it. It's not server components itself complicating our jobs. It's the isomorphic nature of it. The fact that the same code runs in both server and client, and that they're supposed to bridge the gap between them. Supposed to, because we're not there yet.&lt;/p&gt;

&lt;p&gt;Sure, server components will make your react components look simpler. That's because server components come with strict regulations. Don't use hooks, or client state. Don't make them interactive. Guess how complex your client side react components would look, if you'd follow the same rules? Exactly, they'd look simpler too.&lt;/p&gt;

&lt;p&gt;Every time someone tries to sell me the idea that moving code to the server makes our lives easier, they fail to explain how. With server components some go as far as saying that not seeing how they simplify stuff, "is the same resistance we showed against JSX years ago", and that "we just need to adapt our mental model". I can tell you, it's not. Moving anything from client to server, is nothing like moving from HTML or &lt;code&gt;createElement&lt;/code&gt; to JSX.&lt;/p&gt;

&lt;p&gt;I've been writing isomorphic code since Meteor.js was released back in 2012. So I want to be clear that I'm not against fullstack development, and I'm not even against isomorphic development, as I fully embrace its benefits. What I am against, is the false claim that moving parts of our code to the server, would make the job of a frontend developer easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Isomorphic Development: Bridging The Network
&lt;/h2&gt;

&lt;p&gt;Let's clear out the first misconception. Frameworks like Next.js and Remix let you write server and client code neatly together, and you'll boot your environment with a single &lt;code&gt;npm run dev&lt;/code&gt; command. That doesn't mean that it's one environment tho.&lt;/p&gt;

&lt;p&gt;There's still a server, and still a client/browser bundle. In Next the server is code generated by extracting methods like &lt;code&gt;getServerSideProps&lt;/code&gt; or the new functions decorated with &lt;code&gt;'use server'&lt;/code&gt; directives , and in Remix it's functions like &lt;code&gt;loader&lt;/code&gt; and &lt;code&gt;action&lt;/code&gt;. Server code runs on the server, Client code runs in the browser, but depending on your implementation it might also run on the server. Let that last one sink in for a moment. That's what this writing is about.&lt;/p&gt;

&lt;p&gt;The thing to understand is that the build tools separate your server and client logic, and run each in their own environment. Server code on the server, client code in the browser, and sometimes, but not always, client code on the server. That, is what makes it &lt;em&gt;not easier&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;With this paradigm shift, we're not asking frontend developers to render their code on the server, we're asking them to write code that's runs safely, and performant, in both environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging: Juggling Two Environments
&lt;/h2&gt;

&lt;p&gt;With code split between the frontend and backend, you're suddenly managing two separate environments. It means watching two debuggers hitting breakpoints, each with its own context. Or for the console.loggers amongst us, you'll now have log statements in two consoles.&lt;/p&gt;

&lt;p&gt;Also remember that your component might render twice. Once in the context of the server, and once in the context of the client. Might, because Next.js took a different direction than Remix. In Remix, your component will render twice. Once on the server, and then on the client during hydration (attach js handlers to make interactive). Next.js chose the route of not supporting state/interactivity on server components, so no hydration/rerender is needed there. Or at least, not for the server components, client components still need hydration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security: Protecting Boundaries
&lt;/h2&gt;

&lt;p&gt;Fullstack developers must guard against data leaks and breaches. The server has access to sensitive information that clients shouldn't have. You'll now have to think about how to get those properties safely to the client.&lt;/p&gt;

&lt;p&gt;Did you once store a variable on the module scope (out of your react component) as a form of cache between renders? Move that component from 'use client' to 'use server' during a refactor, and you'll leak information between users.&lt;/p&gt;

&lt;p&gt;It might not be hard to fix, but it's an easy bug to introduce when your build tool decides where to run the code. Something to keep in mind and be aware of. At any time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance: The Dual Nature
&lt;/h2&gt;

&lt;p&gt;Performance behaves differently on the client and server. On the client, speed matters and we avoid browser bottlenecks. To use our time efficient, we often work under the principle "you don't run the function a thousand times, so fast is fast enough". Well, move that function from the client to the server, and scalability takes center stage. Functions that once ran only a couple of times, now run a couple times on every user request.&lt;/p&gt;

&lt;p&gt;And remember, Node.js is single threaded. All sync code that runs on the server, block other requests being made. In the browser you don't notice a 10ms delay, on your server, it can cause requests to queue up and your AWS bill to grow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Querying: Bridging The Gap
&lt;/h2&gt;

&lt;p&gt;Querying data is a joint effort. Frontend make API calls, while backend tackles databases. Fullstack developers must master database performance, query optimizations, and indexing. Don't forget about rate limiting challenges when server interacts with external APIs. Where the client would have made a handful requests to an API from thousands of different origins, you're now making thousands requests from a single origin.&lt;/p&gt;

&lt;p&gt;When caching responses for the sake of performance, be aware of where your query runs, for the sake of security. Depending on the query, you might or might not want to share your caches between requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;My concern isn't about moving to fullstack development. I've been there for over 15 years. My concern is about the hybrid nature that we're moving into. Code runs in both environments, and boundaries are there, but not clearly visible. It's easy to slip up because we're busy, or because we're missing something during refactor.&lt;/p&gt;

&lt;p&gt;Our tools aren't ready for this. We don't have an isomorphic debugger that hits breakpoints on both environments, and we don't have an isomorphic console that merges log statements from client and server. Eslint is merely a linter for formatting, and TypeScript won't warn you about leaking variables either. Last time I've checked, we even don't have a code coverage tool merging results from both sides.&lt;/p&gt;

&lt;p&gt;And all of the above can be learned. But it's so much more than "just adapt your mental model". It's new knowledge, it's more knowledge, and it'll make your job harder. If you believe it doesn't, then you're either missing the risks, or you're underselling yourself.&lt;/p&gt;

&lt;p&gt;While we're at it, maybe it's time to stop calling it fullstack development. It's no longer enough to be proficient in both environments. We need to start thinking of it like a single environment, even tho our tools don't expose it that way. How does isomorphic development sound?&lt;/p&gt;




&lt;p&gt;Liked this article? Share your thoughts on &lt;a href="https://twitter.com/intent/tweet?url=https://meijer.ws/articles/isomorphic-development"&gt;Twitter&lt;/a&gt;, and check my other &lt;a href="https://meijer.ws/articles"&gt;articles&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Don't mutate the iterator</title>
      <dc:creator>Stephan Meijer</dc:creator>
      <pubDate>Sat, 08 Jul 2023 10:11:37 +0000</pubDate>
      <link>https://dev.to/smeijer/dont-mutate-the-iterator-308n</link>
      <guid>https://dev.to/smeijer/dont-mutate-the-iterator-308n</guid>
      <description>&lt;p&gt;Or better said, don't mutate the iterator you're iterating over. This is a common mistake that can lead to unexpected results. And yes, despite writing about &lt;a href="https://meijer.ws/articles/dont-stop-mutating"&gt;mutating&lt;/a&gt; and &lt;a href="https://meijer.ws/articles/stop-mutating-in-map-reduce-and-foreach"&gt;not mutating&lt;/a&gt; before, I still &lt;a href="https://twitter.com/meijer_s/status/1676506116736397312"&gt;got bitten&lt;/a&gt; by it. So let's take a look at what happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;I needed to remove all empty headers before a request. For this, I created a function that mutates the &lt;code&gt;request.headers&lt;/code&gt; object. Let's take a look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;deleteEmptyHeaders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;null&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function iterates over the headers, and removes the ones that are "empty". Usage would be like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headers&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;Headers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a&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="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;b&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="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;c&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="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;d&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="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;deleteEmptyHeaders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// { b: '', d: '' } // ?!?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem is that we're mutating the iterator we're iterating over. By removing the first header, the second header shifts up to the first position. Like when you're pulling a book from a pile of books. As the iterator is now in the &lt;em&gt;new&lt;/em&gt; second position, it never processes "header b" but instead processes "header c" as the new second header. And so on.&lt;/p&gt;

&lt;p&gt;It's an old problem that we know of iterating over indexes using a for loop, like &lt;code&gt;for (let i = 0; i &amp;lt; items.length; i++)&lt;/code&gt;. When we remove an item from the array, the indexes shift, and skip items. The same thing happens here. It's just harder to spot.&lt;/p&gt;

&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;p&gt;So how do we fix this? Well, we can't mutate the iterator we're iterating over. So we need to create a new iterator. We can do this by using the &lt;code&gt;Array.from&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;deleteEmptyHeaders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;null&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of &lt;code&gt;Array.from&lt;/code&gt; we can also use the spread operator &lt;code&gt;[...headers.entries()]&lt;/code&gt;, and calling the &lt;code&gt;.entries()&lt;/code&gt; method is optional as it's the default method to be called on such actions. So let's apply this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;deleteEmptyHeaders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Headers&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="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;null&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we don't modify the iterator we're iterating over, and we end up with the expected result. Or said differently, the index of the current header isn't affected by the delete action, as we read a different pile than we modify.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Please do notice that the &lt;code&gt;key&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt; props got reversed when we moved from &lt;code&gt;headers.forEach&lt;/code&gt; to &lt;code&gt;array.forEach&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus
&lt;/h2&gt;

&lt;p&gt;Twitter being Twitter, I got a lot of feedback on my snippet. From folks being unable to spot the bug, to folks saying that a &lt;code&gt;return&lt;/code&gt; in a &lt;code&gt;forEach&lt;/code&gt; is bad practice, to completely burning me down. So let's take a look at some of the feedback.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;folks that write code like this, get what they deserve&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yeah, thanks for the constructive feedback. Now go away. I didn't want to quote this one at first, but it's important to highlight that this is not the way to give feedback. It's not constructive, and it's not helpful. It's just rude.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;don't mutate&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A couple of folks suggested that I shouldn't mutate. Well, I don't have another option. I need to modify the request headers before the request is made. And &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Request/headers"&gt;request.headers&lt;/a&gt; is a read-only property. It is what it is. Tho I agree that generally speaking, modifying the input arguments is not good practice. As always, there are exceptions to the rule.&lt;/p&gt;

&lt;p&gt;The other option would be to not set the header in the first place. If you have that option, go with it. At other times, cleanup is the only option we have.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;don't return in &lt;code&gt;forEach&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I don't get it. The callback is a function. We can use early returns to reduce indentation and keep code readable. I don't see the problem here. Some folks suggested using &lt;code&gt;continue&lt;/code&gt; or &lt;code&gt;break&lt;/code&gt; instead, but that's not going to work. It's a function; we can't use those keywords. Returning from a &lt;code&gt;forEach&lt;/code&gt; to exit early is fine. Note that it won't stop the iteration, but it will stop the execution of the callback.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;key and value are reversed&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yeah, I know. It is the function signature tho. Seriously, why did TC39 decide to put the &lt;code&gt;value&lt;/code&gt; first? It's so confusing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headers&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;headers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// value, key&lt;/span&gt;
&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&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;// key, value&lt;/span&gt;
&lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;you're checking &lt;code&gt;'null'&lt;/code&gt; and &lt;code&gt;'undefined'&lt;/code&gt; instead of &lt;code&gt;null&lt;/code&gt; and &lt;code&gt;undefined&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yep. &lt;code&gt;headers.set&lt;/code&gt; coerces the value into a string. &lt;code&gt;header.set('key', null)&lt;/code&gt; will result in &lt;code&gt;headers.get('key') === 'null'&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;use a common loop&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sure, possible. This is the direct translation, including the same bug. Using &lt;code&gt;forEach&lt;/code&gt; or a traditional loop is about personal preference. Switching between them doesn't change a thing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;deleteEmptyHeaders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;null&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Interestingly enough, this makes the bug easier to spot for me. It's hard to say if that's because I'm used to this pattern or because I now know it's there.&lt;/p&gt;

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

&lt;p&gt;Just a couple of lines and so many opinions and lessons. The "common for loop" could have been suggested in a kinder tone, but seeing this implementation in a traditional loop does remind me why I so strongly prefer those over helper methods like &lt;code&gt;map&lt;/code&gt; and &lt;code&gt;forEach&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is the final solution I came up with and the way I've implemented it in &lt;a href="https://npmjs.com/fetch-addons"&gt;fetch-addons&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;deleteEmptyHeaders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;null&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-api-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-user-id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// string | null | undefined&lt;/span&gt;

&lt;span class="c1"&gt;// somewhere in a middleware&lt;/span&gt;
&lt;span class="nx"&gt;deleteEmptyHeaders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Liked this article? Share your thoughts on &lt;a href="https://twitter.com/intent/tweet?url=https://meijer.ws/articles/dont-mutate-the-iterator"&gt;Twitter&lt;/a&gt;, and check my other &lt;a href="https://meijer.ws/articles"&gt;articles&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Simpler Dev Environments with Procfiles</title>
      <dc:creator>Stephan Meijer</dc:creator>
      <pubDate>Wed, 01 Jun 2022 19:50:55 +0000</pubDate>
      <link>https://dev.to/smeijer/simpler-dev-environments-with-procfiles-2akn</link>
      <guid>https://dev.to/smeijer/simpler-dev-environments-with-procfiles-2akn</guid>
      <description>&lt;p&gt;Throughout the time, I've tried a number of different ways to manage my development environment. From Make files, to &lt;a href="https://dev.to/smeijer/simplify-your-dev-environment-with-pm2-2288"&gt;PM2&lt;/a&gt; and &lt;a href="https://gist.github.com/smeijer/0e3604a51e9393d2ac009cef6ce0fb48"&gt;Tmux&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Tmux, is well.., tmux. &lt;a href="https://github.com/tmuxinator/tmuxinator"&gt;Tmuxinator&lt;/a&gt; makes it manageable, but the "layout" definition is far from readable, and copying errors from history to your clipboard, is a true pain in the ass.&lt;/p&gt;

&lt;p&gt;I still recommend taking a look at PM2 if you need more than "get things running". It comes with a lot of options, but that also means that your config script comes with some verbosity. &lt;/p&gt;

&lt;p&gt;This article explains how I cover my simple use cases. Those that don't require additional file watchers (see PM2), or split panes to separate logs while sticking to a single terminal (see Tmux). For those, I use Procfiles.&lt;/p&gt;

&lt;h2&gt;
  
  
  No fear, Procfile is here!
&lt;/h2&gt;

&lt;p&gt;Procfile? Yeah, the first time I heard it I was "&lt;em&gt;not another make file, right?!&lt;/em&gt;". And luckily, it's nothing like that. Let's get started.&lt;/p&gt;

&lt;p&gt;So a Procfile is a simple &lt;code&gt;key: command&lt;/code&gt; format. And much like a Dockerfile, it's by convention named after the format. I recommend creating file named &lt;code&gt;Procfile&lt;/code&gt; in the root of your project, and commit it to your repo. If not for yourself, than for every new contributor that one day joins your team.&lt;/p&gt;

&lt;p&gt;For &lt;a href="https://magicbell.com"&gt;MagicBell&lt;/a&gt;, our procfile looks as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server: bin/rails s -p 3000 -e development
worker: bin/bundle exec sidekiq
webpack: bin/webpacker-dev-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. That's all the config you need to get multiple services up and running through a single command. &lt;/p&gt;

&lt;p&gt;Obviously, we still need to install a runner to handle this procfile. Meet &lt;a href="https://github.com/ddollar/foreman"&gt;foreman&lt;/a&gt;, or one of it's forks. Foreman is a Ruby script, so for that you'll need to have Ruby installed. There are many forks though. Such as &lt;a href="https://github.com/chrismytton/shoreman"&gt;shoreman&lt;/a&gt;, which is a dependency free shell script, or &lt;a href="https://github.com/strongloop/node-foreman"&gt;node-foreman&lt;/a&gt;, which is a javascript fork. I go with node-foreman, for the simple reason that I'm a node guy and I like that I can &lt;code&gt;npm install&lt;/code&gt; it to the dependencies of my node projects.&lt;/p&gt;

&lt;p&gt;So, go ahead, and install &lt;code&gt;node-foreman&lt;/code&gt;. For the sake of this howto, let's go global. Drop the &lt;code&gt;-g&lt;/code&gt; if you have a concrete project at hand.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i -g node-foreman
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Node-foreman installs a binary named &lt;code&gt;nf&lt;/code&gt;. With this installed, it's as simple as running &lt;code&gt;nf start&lt;/code&gt; in the same directory as your &lt;code&gt;Procfile&lt;/code&gt;. Try it, and see all your services spin up and log to the same terminal. When you have an &lt;code&gt;.env&lt;/code&gt; file in the same directory, &lt;code&gt;node-foreman&lt;/code&gt; automatically loads all environment variables from it. If your env file is named differently, say &lt;code&gt;.env.development&lt;/code&gt;, you can specify it with the &lt;code&gt;--env&lt;/code&gt; flag. Use the &lt;code&gt;--procfile&lt;/code&gt; flag if you need to specify a Procfile.&lt;/p&gt;

&lt;h2&gt;
  
  
  A bit more… tabs
&lt;/h2&gt;

&lt;p&gt;The above might be all you need. But sometimes, I like to have my startup process a bit personalised. For that, I use scripts that I store under my home directory. &lt;/p&gt;

&lt;p&gt;For MagicBell, my config exists of two files. One custom procfile, saved as &lt;code&gt;~/scripts/magicbell.proc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server: bin/rails s -p 3000 -e development
worker: bin/bundle exec sidekiq
webpack: bin/webpacker-dev-server
codegen: yarn codegen -w
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the "executable" script that I run, saved as &lt;code&gt;~/scripts/start-magicbell&lt;/code&gt; (don't forget to &lt;code&gt;chmod +x&lt;/code&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/env bash

cd ~/dev/magicbell/backend
ttab docker-compose up

bundle install
yarn install
yarn db:migrate

nf start -j ~/scripts/magicbell.proc -e .env.development
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When read that script, you'll notice &lt;code&gt;ttab&lt;/code&gt;. &lt;a href="https://www.npmjs.com/package/ttab"&gt;ttab&lt;/a&gt; is a small utility that allows us to start commands in a new terminal tab. That way, I can start postgres &amp;amp; redis in one terminal instance (tab) while running everything else in another. Ttab is optional, but I highly recommend using it for simplicity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i -g ttab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As I've added &lt;code&gt;~/scripts&lt;/code&gt; to my &lt;code&gt;PATH&lt;/code&gt;, I can get the MagicBell environment up and running using a single &lt;code&gt;start-magicbell&lt;/code&gt; command. Let me walk you trough wat it does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;first, it navigates to the git repo on my drive, at &lt;code&gt;~/dev/magicbell/backend&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;runs &lt;code&gt;docker-compose up&lt;/code&gt; in a new tab, detached from the current&lt;/li&gt;
&lt;li&gt;installs all ruby gems, while the db is spinning up&lt;/li&gt;
&lt;li&gt;installs all node modules&lt;/li&gt;
&lt;li&gt;runs our database migration script&lt;/li&gt;
&lt;li&gt;starts the four services defined in the procfile&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this single command, I've started 6 services and ran some install/migrate commands. It happened too often that I manually had to install new gems or modules after pulling changes. Those commands are fast enough to run as part of my startup, yet take enough time for the database to be online before the server starts.&lt;/p&gt;

&lt;p&gt;That's it. Both &lt;a href="https://github.com/strongloop/node-foreman"&gt;node-foreman&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/ttab"&gt;ttab&lt;/a&gt; come with a bunch of options. So be sure to check out their readmes (especially from node-foreman!)&lt;/p&gt;




&lt;p&gt;&lt;em&gt;👋 I'm Stephan. Follow me on &lt;a href="https://twitter.com/meijer_s"&gt;Twitter&lt;/a&gt; if stuff like this has your interest.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Conflicted</title>
      <dc:creator>Stephan Meijer</dc:creator>
      <pubDate>Mon, 28 Mar 2022 21:32:42 +0000</pubDate>
      <link>https://dev.to/smeijer/conflicted-42f2</link>
      <guid>https://dev.to/smeijer/conflicted-42f2</guid>
      <description>&lt;p&gt;A LOT has happened over the last few months. And yet, I haven't written about any of it. I wanted to, but I've been unable to find the time, the words, or maybe mostly the right mood.&lt;/p&gt;

&lt;p&gt;I wanted to write about &lt;a href="https://twitter.com/meijer_s/status/1445117911681601544"&gt;my adventure with Kent C. Dodds&lt;/a&gt;. We've created one of the first, if not the first, full-fletched website using Remix. It was lots of fun. Little did I know that the animations and transitions that I implemented for Kent &lt;a href="https://twitter.com/unamashana/status/1446133575087132682"&gt;would lead&lt;/a&gt; to my current position at MagicBell.&lt;/p&gt;

&lt;p&gt;Together with the launch of his new website, my &lt;a href="https://kentcdodds.com/chats/04/19/stephan-meijer-chats-about-side-projects"&gt;"Chat with Kent"&lt;/a&gt; got published. Again, something worth writing about. I remember thinking back then that I should have said things differently. But I still haven't listened to the recording.&lt;/p&gt;

&lt;p&gt;The launch of his site lead to a chat with &lt;a href="https://twitter.com/unamashana"&gt;Hana Mohan&lt;/a&gt;. Not long after, I signed for a role as Founding Frontend Developer at &lt;a href="https://www.magicbell.com"&gt;MagicBell&lt;/a&gt;, and ended my partnership at a firm that I co-founded but got never recognized for. I've sold my 10% and moved on.&lt;/p&gt;

&lt;p&gt;As the cherry on the pie, we had &lt;a href="https://twitter.com/unamashana/status/1491346977476743170"&gt;a company retreat&lt;/a&gt; in Miami. It was a fantastic way to get to know my new coworkers. I'm truly grateful for that experience. That trip alone is worth writing for.&lt;/p&gt;

&lt;p&gt;Since I started working for MagicBell, I've added several features to the product, the dashboard, the SDKs, and launched &lt;a href="https://twitter.com/meijer_s/status/1499754505323302916"&gt;MagicBell Playground&lt;/a&gt;. A little side project under business hours.&lt;/p&gt;

&lt;p&gt;But then the conflicted part.&lt;/p&gt;

&lt;p&gt;While working on Kent's new website, my step-dad got diagnosed with cancer. He is unlikely to make the end of this year. Doctors have given him months, and he's running out of time.&lt;/p&gt;

&lt;p&gt;While I quit my partnership and transitioned to my new position at MagicBell, my wife booked a last-minute flight "home," as her grandma was hospitalised with Covid, and doctors gave her a 50/50 chance of survival. Luckily she did.&lt;/p&gt;

&lt;p&gt;Only to be abruptly awakened by incoming Russian artillery on February 24th. Two days after my 35th birthday. While I launched the playground, grandma hid in the bathtub. She's here now. Safe with us. But it took us two weeks to get her out of the country.&lt;/p&gt;

&lt;p&gt;We managed to extract six more friends. Or, well, one friend with her two kids and their grandma and one aunt with her grandson. Other friends fled on time or are still stuck in this dirty war. Some because they don't want to leave their home. Others because they simply can't.&lt;/p&gt;

&lt;p&gt;As I'm writing this, grandma is crying again. She received a photo of her apartment earlier today, and it doesn't look nice.&lt;/p&gt;

&lt;p&gt;The last few months have been an emotional rollercoaster. My professional life has been nothing less than amazing. Sure, there are some &lt;a href="https://twitter.com/meijer_s/status/1504483086288908295"&gt;frustrations&lt;/a&gt;, but that's nothing. On a personal level, I do my best to be happy and positive for the kids, while I'm heartbroken for the lives of our friends.&lt;/p&gt;

&lt;p&gt;In a few days from now, my kid turns 2. He'll get his first bike, and we'll eat pie. And then again, one of the guests will be our friend with her kids, who lost their home, miss their dad, and wear our kids' clothes.&lt;/p&gt;

&lt;p&gt;I'm grateful. I'm happy. I'm terrified. I wish I could do more. &lt;/p&gt;

</description>
      <category>career</category>
      <category>personal</category>
    </item>
    <item>
      <title>Typed GraphQL with react-query &amp; graphql-request</title>
      <dc:creator>Stephan Meijer</dc:creator>
      <pubDate>Thu, 25 Nov 2021 10:19:13 +0000</pubDate>
      <link>https://dev.to/smeijer/typed-graphql-with-react-query-graphql-request-3jj9</link>
      <guid>https://dev.to/smeijer/typed-graphql-with-react-query-graphql-request-3jj9</guid>
      <description>&lt;p&gt;While our front-end at &lt;a href="https://magicbell.io/"&gt;MagicBell&lt;/a&gt; is a React/TypeScript application, we write our back-end in Ruby. This article explains how I've connected our client to the back-end and how we guard against breaking API changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  GraphQL Code Generator
&lt;/h2&gt;

&lt;p&gt;Let's get the obvious choice out of the way first. &lt;a href="https://www.graphql-code-generator.com/"&gt;&lt;code&gt;graphql-codegen&lt;/code&gt;&lt;/a&gt;. It's a no-brainer. This tool enables us to generate TypeScript types based on a GraphQL schema. Think typed queries, mutations, fragments, and object types.&lt;/p&gt;

&lt;p&gt;Having types generated based upon your GraphQL schema means that TypeScript will inform you when the back-end (GraphQL API) introduces a breaking change. As a front-end engineer, that's what I want!&lt;/p&gt;

&lt;p&gt;GraphQL Code Generator can generate fully typed React hooks if you tell it to, but I'm a fan of keeping things simple and thereby of their &lt;a href="https://the-guild.dev/blog/typed-document-node"&gt;TypedDocumentNode approach&lt;/a&gt;. This variant is unaware of the GraphQL client that you're using. In other words, it's not tied to &lt;code&gt;react-apollo&lt;/code&gt; (or alternative).&lt;/p&gt;

&lt;p&gt;To get that up and running, you'll need to install a few dev dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i -D 
  @graphql-codegen/cli 
  @graphql-codegen/typed-document-node
  @graphql-codegen/typescript 
  @graphql-codegen/typescript-operations 
  @graphql-typed-document-node/core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now the more exciting part, our codegen config (&lt;code&gt;codegen.yml&lt;/code&gt;). We don't have a schema stored in code, and because the back-end is in Ruby, &lt;code&gt;graphql-codegen&lt;/code&gt; won't be able to extract the types out of the back-end source files either. So instead, we provide our GraphQL endpoint. It's an easy setup, but the one downside is that our server must be running when we want to generate new types.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;http://localhost:3000/graphql:&lt;/span&gt;
      &lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;X-MAGICBELL-API-KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${MAGICBELL_API_KEY}"&lt;/span&gt;
&lt;span class="na"&gt;documents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./app/javascript/src/graphql/**/*.graphql"&lt;/span&gt;
&lt;span class="na"&gt;generates&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;./app/javascript/src/graphql/generated.ts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;typescript&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;typescript-operations&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;typed-document-node&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Codegen will read the &lt;code&gt;.env&lt;/code&gt; file from the project root and use that to populate the API headers. &lt;code&gt;documents&lt;/code&gt; is the path where the GraphQL queries are stored. I'll come back to those later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Graphql Client
&lt;/h2&gt;

&lt;p&gt;I'm not going with the famous &lt;code&gt;@apollo/client&lt;/code&gt; because it's too much for our dashboard. Besides, we're migrating from REST to GraphQL, meaning we'll need to deal with both for some time. As &lt;a href="https://react-query.tanstack.com/"&gt;React Query&lt;/a&gt; is sublime in cache management, and can be used for both REST and GraphQL, we'll be using that.&lt;/p&gt;

&lt;p&gt;I have considered &lt;a href="https://github.com/vercel/swr"&gt;&lt;code&gt;swr&lt;/code&gt;&lt;/a&gt; instead of &lt;code&gt;react-query&lt;/code&gt;, as it's smaller, but they lack some fundamentals that we need. Think clear state indicators or even a (solid) solution to manage mutations.&lt;/p&gt;

&lt;p&gt;For the fetching part, we'll use &lt;a href="https://github.com/prisma-labs/graphql-request"&gt;&lt;code&gt;graphql-request&lt;/code&gt;&lt;/a&gt;. It has almost as many installs/month as apollo, but it's &lt;a href="https://bundlephobia.com/package/graphql-request@3.6.1"&gt;way smaller&lt;/a&gt; and doesn't have as many open issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  TypedDocumentNode and graphql-request
&lt;/h3&gt;

&lt;p&gt;Now the "tricky" part, we need &lt;code&gt;graphql-request&lt;/code&gt; to use our generated &lt;code&gt;TypedDocumentNode&lt;/code&gt;. For that, I've created a custom hook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useCallback&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;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;request&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;graphql-request&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RequestDocument&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;graphql-request/dist/types&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;TypedDocumentNode&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;@graphql-typed-document-node/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useCurrentUser&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;../context&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;useGraphqlRequest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useCurrentUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TDocument&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TVariables&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RequestDocument&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;TypedDocumentNode&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TDocument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TVariables&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;variables&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;TVariables&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="nx"&gt;request&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TDocument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TVariables&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/graphql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-MAGICBELL-API-KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;apiKey&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="nx"&gt;apiKey&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 the magic sauce that will power it all. It returns a &lt;code&gt;graphql-request&lt;/code&gt; client and uses &lt;code&gt;TypedDocumentNode&lt;/code&gt; to infer types from the query. It also provides some defaults to &lt;code&gt;graphql-request&lt;/code&gt;. Even without the type annotations, this would be a helpful hook that prevents setting options (like the header) in multiple places throughout our code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing queries
&lt;/h3&gt;

&lt;p&gt;And with this setup, we have a way to query the back-end with confidence. To create a new query, I'll write a GraphQL definition in a &lt;code&gt;*.graphql&lt;/code&gt; file somewhere in the path declared by &lt;code&gt;codegen.yml#documents&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, this &lt;a href="https://www.magicbell.com/docs/graphql-api/reference#logs-query"&gt;logs query&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;logMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;createdAt&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;lastName&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;actionUrl&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run &lt;code&gt;npx graphql-codegen&lt;/code&gt;, and consume the generated types in my query hook that I compose using &lt;code&gt;react-query&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useQuery&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;react-query&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useGraphqlRequest&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;./useGraphqlRequest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LogMessageDocument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;LogMessageQuery&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./generated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;UseLogMessageOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;logId&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;LogMessageQuery&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;useLogMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;logId&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;UseLogMessageOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useGraphqlRequest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LogMessageQuery&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;log-message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logId&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="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;LogMessageDocument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&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;logId&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;logId&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, we'll consume that hook in our component, to fetch the data that we need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;LogDetails&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;logId&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;LogDetailsProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useLogMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;logId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it. &lt;code&gt;data&lt;/code&gt; is fully typed. Suppose the back-end engineers introduce a change that isn't compatible with the current front-end, or I'm consuming data that never existed in the first place. In that case, TypeScript will notify us by throwing errors as part of the checks that run against our pull requests.&lt;/p&gt;

&lt;h1&gt;
  
  
  Recap
&lt;/h1&gt;

&lt;p&gt;We type our GraphQL queries using &lt;code&gt;graphql-codegen&lt;/code&gt; and use &lt;code&gt;react-query&lt;/code&gt; to manage server/query state. &lt;code&gt;graphql-request&lt;/code&gt; is the glue between codegen and react-query, a typed &lt;code&gt;fetch&lt;/code&gt; for GraphQL, if you want. And with this setup, I have reduced the chance of breaking our GraphQL queries at &lt;a href="https://magicbell.io/"&gt;MagicBell&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>node</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>api</category>
    </item>
    <item>
      <title>Handling POST requests in Next.js getServerSideProps</title>
      <dc:creator>Stephan Meijer</dc:creator>
      <pubDate>Fri, 10 Sep 2021 08:11:56 +0000</pubDate>
      <link>https://dev.to/smeijer/handling-post-requests-in-next-js-getserversideprops-50ia</link>
      <guid>https://dev.to/smeijer/handling-post-requests-in-next-js-getserversideprops-50ia</guid>
      <description>&lt;p&gt;The documentation pages of Next.js list &lt;a href="https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering"&gt;&lt;code&gt;getServerSideProps&lt;/code&gt;&lt;/a&gt; under "data fetching", and for any kind of data mutations, they'll point you to the &lt;a href="https://nextjs.org/docs/api-routes/introduction"&gt;&lt;code&gt;api routes&lt;/code&gt;&lt;/a&gt;. I'm here to tell you, that there is another way!&lt;/p&gt;

&lt;p&gt;In this article, I'm going to show you how you can post to &lt;code&gt;getServerSideProps&lt;/code&gt;, and have your components, data fetching, and data mutation logic all collocated in the same file. &lt;/p&gt;

&lt;h2&gt;
  
  
  HTML Forms
&lt;/h2&gt;

&lt;p&gt;I'll directly dive in. We'll create a form so we can bring this to a working example. Let's keep it simple. Create a new next app with &lt;code&gt;npx create-next-app&lt;/code&gt;, and add a form to your index component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;IndexPage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;person&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"post"&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;"name"&lt;/span&gt; &lt;span class="na"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&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;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;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that I don't set the "action" attribute. When the action attribute is left out, it defaults to the current path. And that's exactly what we want it to do. If you'd be using an hosted form service like &lt;a href="https://rake.red"&gt;rake.red&lt;/a&gt;, you'd be setting it to an absolute url.&lt;/p&gt;

&lt;p&gt;As we'll be adding &lt;code&gt;getServerSideProps&lt;/code&gt; in the next step, I've also already provided an initial &lt;code&gt;name&lt;/code&gt; value via the props, and default it to &lt;code&gt;person&lt;/code&gt; for now. &lt;/p&gt;

&lt;p&gt;That's it for the component part. This will render a form that accepts a name, and will be submitted to the current path. Give it a spin!&lt;/p&gt;

&lt;h2&gt;
  
  
  Server Side Props
&lt;/h2&gt;

&lt;p&gt;So let's start with the basic variant of &lt;code&gt;getServerSideProps&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getServerSideProps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&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;method&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;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;smeijer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've added an initial value for &lt;code&gt;name&lt;/code&gt;, which you'll now see rendered in your form. The value no longer comes from the component, but it's consumed from an "backend part". See it as a query if you will, but without additional request, and without a need for react-query, swr, or other forms of client state management. &lt;/p&gt;

&lt;p&gt;Now, if you refresh the page, and resubmit the form, you'll notice that the server will log &lt;code&gt;GET&lt;/code&gt; on refresh, and &lt;code&gt;POST&lt;/code&gt; on form submit. That's right! We don't need anything special to receive the post. Next already does that out of the box.&lt;/p&gt;

&lt;p&gt;But here's the thing. You can add logging statements for &lt;code&gt;req.body&lt;/code&gt;, &lt;code&gt;req.params&lt;/code&gt; and &lt;code&gt;req.query&lt;/code&gt;, but they're all empty. Next.js does not process our request body. So that's something we're gonna fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hooking up body-parser
&lt;/h2&gt;

&lt;p&gt;We'll be using &lt;a href="https://www.npmjs.com/package/body-parser"&gt;&lt;code&gt;body-parser&lt;/code&gt;&lt;/a&gt; to read the request body. Install the dependency, and add the following 3 lines to the top of your file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;bodyParser&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;body-parser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;util&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;util&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;getBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;promisify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bodyParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urlencoded&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;urlencoded()&lt;/code&gt; returns an middleware function that has the common 3 arguments, &lt;code&gt;request&lt;/code&gt;, &lt;code&gt;response&lt;/code&gt; and &lt;code&gt;next&lt;/code&gt;. We use &lt;code&gt;promisify&lt;/code&gt; to turn the callback function into an async function, because we won't be using this as a traditional middleware.&lt;/p&gt;

&lt;p&gt;If you'll now update &lt;code&gt;getServerSideProps&lt;/code&gt; and add &lt;code&gt;await getBody(req, res)&lt;/code&gt; to just before that log statement, you'll see that our request body has been processed. 🤯&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getServerSideProps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;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="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;getBody&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&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;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// POST { name: 'smeijer' }&lt;/span&gt;
  &lt;span class="c1"&gt;// …&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now you're able to use &lt;code&gt;getServerSideProps&lt;/code&gt; to handle your mutations. Seriously, do with the data in &lt;code&gt;req.body&lt;/code&gt;, whatever you like. You can access secrets from &lt;code&gt;process.env&lt;/code&gt;, and connect to the database in &lt;code&gt;getServerSideProps&lt;/code&gt; just fine. It only runs on the server, and won't be exposed to the client.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full Example
&lt;/h2&gt;

&lt;p&gt;Here is the full, runnable, Next.js page file. You'll notice that I've wrapped the body parser statement inside a check against the request method. That's technically not required, but let's just add the body parsing in there so it doesn't do unnecessary work during GET requests. Besides, we're likely to add more logic in the POST handler, like writing stuff to our database.&lt;/p&gt;

&lt;p&gt;Depending on your needs, you can return completely different props, or a similar shape. To keep things predictable, I definitely recommend the latter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;bodyParser&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;body-parser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;promisify&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;util&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;getBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;promisify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bodyParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urlencoded&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getServerSideProps&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="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;getBody&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="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&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;body&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;smeijer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;received!&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="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;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="nx"&gt;IndexPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&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;&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;method&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"post"&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;"name"&lt;/span&gt; &lt;span class="na"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;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;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final word
&lt;/h2&gt;

&lt;p&gt;Even though Next.js doesn't advertise the &lt;code&gt;getServerSideProps&lt;/code&gt; handler in this way, I think it can be very useful as a simple way to add forms to your page, without the need to define api routes, and deal with client state.&lt;/p&gt;

&lt;p&gt;I also haven't tried if this works when hosting on vercel, as I'm running my Next.js apps on my own VPS instead of "serverless". So if anyone can give that a shot, and let us know if it worked out, that would be great.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;👋 I'm Stephan, and I'm building &lt;a href="https://metricmouse.com"&gt;metricmouse.com&lt;/a&gt;. If you wish to read more of mine, follow me on &lt;a href="https://twitter.com/meijer_s"&gt;Twitter&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>nextjs</category>
      <category>api</category>
    </item>
    <item>
      <title>Typescript Type Assertions</title>
      <dc:creator>Stephan Meijer</dc:creator>
      <pubDate>Tue, 07 Sep 2021 15:29:28 +0000</pubDate>
      <link>https://dev.to/smeijer/typescript-type-assertions-4klf</link>
      <guid>https://dev.to/smeijer/typescript-type-assertions-4klf</guid>
      <description>&lt;p&gt;Type assertions look a lot like of &lt;a href="https://dev.to/smeijer/typescript-type-guards-and-type-predicates-4m5e"&gt;type guards&lt;/a&gt;, with the exception that they don't need to be embedded in an &lt;code&gt;if&lt;/code&gt; statement.&lt;/p&gt;

&lt;p&gt;Imagine we have a blog, and allow authenticated users to post comments. We come up with a function like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;isAuthenticated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;addComment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isAuthenticated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unauthenticated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;comment&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have extracted an &lt;code&gt;isAuthenticated&lt;/code&gt; helper that tells us if the user is logged in. And we make sure to throw an error if they are not. &lt;/p&gt;

&lt;p&gt;In pure JavaScript, we would be done by now. An error will be thrown if &lt;code&gt;user&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt;, so by the time we reach the database statement,  we're sure that the &lt;code&gt;user&lt;/code&gt; object is defined.&lt;/p&gt;

&lt;p&gt;TypeScript on the other hand, still sees the &lt;code&gt;user&lt;/code&gt; as &lt;code&gt;User | null&lt;/code&gt;. To fix that, we can introduce a type guard. Update the helper by adding the type predicate &lt;code&gt;user is User&lt;/code&gt; , and it understands that user is &lt;code&gt;null&lt;/code&gt; in the scope of the &lt;code&gt;if&lt;/code&gt; statement,  and thereby has to be &lt;code&gt;User&lt;/code&gt; after it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;isAuthenticated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I recommend reading my earlier article about &lt;a href="https://dev.to/smeijer/typescript-type-guards-and-type-predicates-4m5e"&gt;Type guards and Type predicates&lt;/a&gt; if you're unfamiliar with those.&lt;/p&gt;

&lt;p&gt;By adding the type predicate we've fixed the issue in the database statement. TypeScript is aware that &lt;code&gt;user&lt;/code&gt; will never be &lt;code&gt;null&lt;/code&gt; at that stage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assertion Functions
&lt;/h2&gt;

&lt;p&gt;The problem lies in repetition. Having those 3 lines of code all around the project, adds noise. Besides, it's unlikely that this is the only check you have defined. &lt;/p&gt;

&lt;p&gt;We could turn that check into something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;assertAuthenticated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unauthenticated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;addComment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;assertAuthenticated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;comment&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;In plain JavaScript, that would still work.  The &lt;code&gt;assertAuthenticated&lt;/code&gt; function throws if the &lt;code&gt;user&lt;/code&gt; object is not defined, and because the error propagates, we never reach the database statement.&lt;/p&gt;

&lt;p&gt;However, because we removed the wrapping &lt;code&gt;if&lt;/code&gt; statement, TypeScript is again not happy. The &lt;code&gt;user._id&lt;/code&gt; in the database statement, will throw an &lt;code&gt;TS2531: Object is possibly 'null'&lt;/code&gt;. To fix that, we'll insert the &lt;a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#assertion-functions"&gt;&lt;code&gt;Type assertion&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's quite trivial, really. Simply add &lt;code&gt;asserts&lt;/code&gt; in front of the type predicate, and remove the return statement from the assertion function. Where type guards must return a &lt;code&gt;boolean&lt;/code&gt;, assertion functions must return &lt;code&gt;void&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;assertAuthenticated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;asserts&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unauthenticated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now when you call this function, TypeScript knows that the value of &lt;code&gt;user&lt;/code&gt; can never be &lt;code&gt;null&lt;/code&gt; after that line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;addComment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;assertAuthenticated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;comment&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Assert Generic
&lt;/h2&gt;

&lt;p&gt;Now that we know about &lt;code&gt;asserts&lt;/code&gt;, we can also quite easily introduce a reusable helper function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&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;condition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;asserts&lt;/span&gt; &lt;span class="nx"&gt;condition&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;Exclude&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;condition&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;condition&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then whenever you have a function that accepts optional or partial values, you can simply protect them using a this &lt;code&gt;assert&lt;/code&gt; helper.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;latestBlog&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blog&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;blogs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;smeijer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// Blog | null&lt;/span&gt;
  &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;author does  not have any blogs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// and here we know `blog: Blog`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the &lt;code&gt;blog&lt;/code&gt; is not found in the database, the &lt;code&gt;assert&lt;/code&gt; statement will throw an error up the chain, if it did find something, we can safely work with the object after the assert call.&lt;/p&gt;

&lt;p&gt;The next time you consider type casting a property with &lt;code&gt;as MyType&lt;/code&gt;, consider writing an type assertion instead. Instead of simply silencing TypeScript, you'll get runtime validation with a single line of code.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;👋 I'm Stephan, and I'm building &lt;a href="https://metricmouse.com"&gt;metricmouse.com&lt;/a&gt;. If you wish to read more of mine, follow me on &lt;a href="https://twitter.com/meijer_s"&gt;Twitter&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How I got a job offer from a funded startup</title>
      <dc:creator>Stephan Meijer</dc:creator>
      <pubDate>Thu, 06 May 2021 14:23:23 +0000</pubDate>
      <link>https://dev.to/smeijer/how-i-got-a-job-offer-from-a-funded-startup-3a56</link>
      <guid>https://dev.to/smeijer/how-i-got-a-job-offer-from-a-funded-startup-3a56</guid>
      <description>&lt;p&gt;I recently got an exciting job offer, that I had to decline. This article is my attempt to share how it came to that, and what I've learned from it. &lt;/p&gt;

&lt;h2&gt;
  
  
  How it started
&lt;/h2&gt;

&lt;p&gt;It all started with a simple tweet about a tool that I wanted to share with all of you. As the company in question asked me not to mention them in this article, I need to keep this intro a bit vague. But, that's fine. This article is about the experience, not about &lt;em&gt;"the startup"&lt;/em&gt; in question.&lt;/p&gt;

&lt;p&gt;Shortly after that tweet, they send me a DM. They are trying to build a community around the tool and were wondering whether I remembered how/where I learned about its existence. After a few messages back and forth, they invited me to their Discord.&lt;/p&gt;

&lt;h2&gt;
  
  
  The invite
&lt;/h2&gt;

&lt;p&gt;There weren't many users in the Discord, and we were just chatting a bit about random dev stuff. In the meanwhile, I was reading a bit about &lt;em&gt;the startup&lt;/em&gt; and found out they were looking for people. I didn't consider switching jobs at the time. But as a maker, I was curious about how the development of an open source cli utility contributes to building a sustainable business.&lt;/p&gt;

&lt;p&gt;They explained me that there hasn't been an official announcement, but that they were working on an companion product. They also mentioned that they've noticed that I've &lt;em&gt;"built some impressive stuff like &lt;a href="https://updrafts.app"&gt;updrafts.app&lt;/a&gt;"&lt;/em&gt;, and were happy to chat if I was interested in any of the positions.&lt;/p&gt;

&lt;p&gt;I never expected that my question about their business would lead to an invite to chat. I know this is something small. But I had a moment of pride. Honestly, hearing or reading someone else say that they noticed your work, and found it impressive, feels amazing. I wish people would compliment each other more often. I'll never know for sure, but I like to think that it was my side projects that opened this door.&lt;/p&gt;

&lt;p&gt;Anyways, I wasn't looking to switch jobs. So that's where the story ended.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's talk
&lt;/h2&gt;

&lt;p&gt;Fast forward to April '21. They send out a tweet mentioning that they were looking to expand the team. As I also operate on the hiring side, it's interesting for me to know what vacancies are out there, and what financial compensation companies offer. I see reading vacancies as part of my job, but I never apply as part of it. I want to stay respectful of people's time. Going into an interview when I know there is no chance of success, is a waste of time and I find that disrespectful.&lt;/p&gt;

&lt;p&gt;Their vacancies mentioned &lt;em&gt;"Competitive salary and meaningful stock options"&lt;/em&gt;. So I went to Discord and asked if they had more specifics about that. Almost instantly, they replied with their salary range, and the range of stock options one would receive. That message was later removed. Although I don't agree with hiding salaries, I won't quote it out of respect to them. In that same minute, they also mentioned that they were &lt;em&gt;"happy to hop on Zoom"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It took me 20 minutes to come up with a proper response to that message. As I mentioned, I usually don't jump into calls, because I don't want to waste time if I already know it won't be a fruitful talk. But something was different this time. One of the vacancies was like it was written towards my profile, and their salary range looked promising. &lt;em&gt;The startup&lt;/em&gt; felt like an awesome company to work for. I was honestly interested, so I decided to send them a message to let them know I was ready to chat.&lt;/p&gt;

&lt;h2&gt;
  
  
  First chat
&lt;/h2&gt;

&lt;p&gt;And that was honestly all I expected. We scheduled a 30-minute chat, and I expected that we would talk about what kind of company &lt;em&gt;the startup&lt;/em&gt; is, and what roles they were looking to fill. Time was flying by, and our 30-minute chat turned into a 3-hour talk. I got really excited about this!&lt;/p&gt;

&lt;p&gt;We talked a bit about &lt;em&gt;the startup&lt;/em&gt;, how they plan to grow it into a sustainable business, and we talked a bit about my day job. I think those subjects would have fitted perfectly within that 30-minute window. If only we didn't start the conversation around my open source work, side projects, and articles that I've been publishing.&lt;/p&gt;

&lt;p&gt;During this first chat, it became clear that we were a perfect match. I'm able to bootstrap applications, I'm independent, can move fast, and have experience with early-stage startups. I check all the boxes in the profile that they're looking for. I don't mean it to brag. We were just so compatible, that I got more and more excited about this by the minute. It was like love at first sight but on a professional level.&lt;/p&gt;

&lt;p&gt;We ended our call with the notice that we would both digest the conversation, and would come back to it later.&lt;/p&gt;

&lt;h2&gt;
  
  
  They bought a license!
&lt;/h2&gt;

&lt;p&gt;A day after our call, I noticed that they bought a license for &lt;a href="https://updrafts.app"&gt;updrafts.app&lt;/a&gt;, and created an account at &lt;a href="https://rake.red"&gt;rake.red&lt;/a&gt;. I reached out to them on Discord, mentioned that I noticed their purchase and that I'm happy to jump on another call to walk them through what it has to offer.&lt;/p&gt;

&lt;p&gt;They mentioned they'll keep digging for a while, but would like to schedule the next call to continue our conversation from the day before. And so we did.&lt;/p&gt;

&lt;p&gt;That second call didn't last long. They mentioned that they were impressed by my work. They liked how I'm able to solve my own problems and turn them into products that can be monetized. They found my articles structured, well written, and I'm able to explain complex subjects in simple terms. I planned to ask them what they thought of the products, but it completely slipped my mind.&lt;/p&gt;

&lt;p&gt;The call took about 15 - 20 minutes, and we ended with an agreement that I'd do a test task.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Task
&lt;/h2&gt;

&lt;p&gt;Even though they were honestly excited about my side projects, open-source work, and articles, it wasn't enough to exempt me from doing a test task. Their task involved &lt;em&gt;"Creating a prototype of an interactive API documentation that can be used for user testing".&lt;/em&gt; The description was a bit vague, and if I already had the job, I'd definitely jump on a call to get a better understanding of what they had in mind. But I approached it as a chance to show that I'm able to turn a vague customer description into a product. I believe that's what distinguishes me from most other developers.&lt;/p&gt;

&lt;p&gt;I did ask them how much time they'd expect candidates to spend on the task, to which they replied that that &lt;em&gt;"It’s very individual, typically 3–10 hours"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Honestly, creating something useful in 3 hours sounds impossible to me. But I was confident that I could create something in ~8 hours (a typical workday). And so I did. I &lt;a href="https://twitter.com/meijer_s/status/1384960782069342211"&gt;tweeted about my prototype&lt;/a&gt;, and they send a DM that they liked what they were seeing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nailed It!
&lt;/h2&gt;

&lt;p&gt;Exactly a week after the second call, we had our third call. We talked about the test task, and I explained how I interpreted the task and why I've built the site the way I did. They mentioned that I found the right balance between trade-offs, spend my time on the right parts, and kind of nailed it. They also mentioned that ~8 hours is the sweet spot to spend on a task like this. &lt;/p&gt;

&lt;p&gt;When we were done talking about the task, I came back to that forgotten question from call 2. &lt;em&gt;"You've tried my products, what do you think of them?"&lt;/em&gt; I got some useful feedback out of that, and some more compliments. Which isn't why I asked about it, but it felt nice nevertheless.&lt;/p&gt;

&lt;p&gt;Shortly after the third call, I had a fourth to meet one of the colleagues. It was a more informal chat, meant as a "get to know each other".&lt;/p&gt;

&lt;p&gt;Three days later they made me an offer, which I later declined due to personal reasons.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;I hear you think. Okay, nice story. But why did I read this? Well, I've learned a few things, and I was working towards that. So let me share what I got out of this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Visibility for the win! For the bigger part of my career, I've been invisible. Working as a one-man team on a business-to-business collaboration platform, while not hearing anything from anyone. Mostly triggered by covid, I've moved to platforms like &lt;a href="https://meijer.ws/twitter"&gt;Twitter&lt;/a&gt; and &lt;a href="http://kcd.im/discord"&gt;Discord&lt;/a&gt; to satisfy my hunger for a bit of social contact. Since then, I tweet occasionally and feel less burdened to send an innocent message like &lt;em&gt;"got any details?"&lt;/em&gt;. A year ago, I wouldn't have expected this, but seriously, it opens doors! Just make yourself visible. By showing your work, responding to people, or just a small chat. It doesn't matter how, but stay respectful. It's not about having thousands of followers either. It's about showing who you are.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I believe I nailed this one because I'm confident that I'm a match. I also &lt;a href="https://dev.to/smeijer/trivia-or-job-interview-4mpj"&gt;know how to screw up&lt;/a&gt;, but overall I have a good score of hit/miss ratio. One thing that keeps coming back, is that I fail at the interviews that come up with white-board or trivia questions. But prepare a drink, and chat with me about my experiences, apps that I've built, ask me how I would solve a customer's problem, and I'm able to convince you of my strength. Not because I'm a show-off. But because I'm confident in what I do. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Test Tasks suck! It took 8 hours of my time. Trivia sucks more, but this ain't awesome either. In my case, it was okay. I liked this task, and I can see myself building it into another (open-source) side project. The whole experience was motivational, and I got tons of new ideas. But I would never ask a candidate to do the same. Especially not as vague as this one was. And there it was again. The Trivia. Should I ask for more details to build exactly what they expect? Or would the simple fact of asking, make me fail this round?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When possible, stay in touch! I think one of the nice things about this experience was that it was just so informal. I didn't wait for the next call to let them know I noticed their purchase of one of my apps. And they didn't wait for the next call to let me know that they liked my task result. This back and forth messaging between the calls, has a binding effect. It strengthens your relationships and thereby improves your chance of success. But again, don't overdo it. Stay respectful and professional.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Words
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;The startup&lt;/em&gt; felt like an awesome company to work for. I didn't accept the position, but I can imagine that there will be days that I'm thinking back to this experience with the thought that I've made the wrong choice.&lt;/p&gt;

&lt;p&gt;The calls were nice, and the task was highly relevant for the position that I was applying for. If you're looking for a job, I recommend taking a look at... &lt;/p&gt;

&lt;p&gt;Sorry, this is where it ends. They asked me to remove all references to their company and personal names. So I can't point you to their jobs page, or discord. Follow me instead? You can find me on &lt;a href="https://meijer.ws/twitter"&gt;Twitter&lt;/a&gt; and my other articles are listed at &lt;a href="https://meijer.ws/articles"&gt;meijer.ws/articles&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>personal</category>
      <category>career</category>
      <category>learning</category>
    </item>
    <item>
      <title>Trivia or Job Interview?</title>
      <dc:creator>Stephan Meijer</dc:creator>
      <pubDate>Tue, 04 May 2021 20:38:38 +0000</pubDate>
      <link>https://dev.to/smeijer/trivia-or-job-interview-4mpj</link>
      <guid>https://dev.to/smeijer/trivia-or-job-interview-4mpj</guid>
      <description>&lt;p&gt;I've built multiple SaaS, and some are used by multinationals. Yet, I fail miserably at tricky interview questions. In this article, I'm going to show you a few recent questions I got, and share my thoughts.&lt;/p&gt;

&lt;p&gt;In case you haven't seen any of my work. In the last 12 months, I've launched &lt;a href="https://testing-playground.com" rel="noopener noreferrer"&gt;testing-playground.com&lt;/a&gt;, &lt;a href="https://updrafts.app" rel="noopener noreferrer"&gt;updrafts.app&lt;/a&gt;, &lt;a href="https://rake.red" rel="noopener noreferrer"&gt;rake.red&lt;/a&gt; and a bunch of &lt;a href="https://meijer.ws/open-source" rel="noopener noreferrer"&gt;open source projects&lt;/a&gt;. I like to believe that I know what I'm talking about.&lt;/p&gt;

&lt;h1&gt;
  
  
  Question 1
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Here is a simple design of a navbar menu, we are struggling to stick the &lt;code&gt;login&lt;/code&gt; button to the right edge of the &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt; (borders are added for a better understanding).&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Home&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Products&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;About&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Log in&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;nav&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.4rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.4rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Options - Single Choice&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;a:last-of-type { margin-left: auto; }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;a:last-of-type { margin-left: 100%; }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;a:last-of-type { float: right; }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;a:last-child { margin-left: auto; }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nav:last-child { float: right; }&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My thoughts
&lt;/h2&gt;

&lt;p&gt;The interview existed of 12 questions, that had to be answered within 16 minutes. That sounds doable, until you meet the trick questions.&lt;/p&gt;

&lt;p&gt;Have you thought about the question? I think that &lt;code&gt;a:last-child { margin-left: auto; }&lt;/code&gt; is the correct answer. But &lt;code&gt;a:last-of-type { margin-left: auto; }&lt;/code&gt; works as well. As this is an automated interview, I can only hope that I choose whatever the interviewer prefers. As &lt;code&gt;a:last-of-type&lt;/code&gt; is listed first, this is easy to get tricked by. Time is limited, so when the candidate is confident that an option works, they are going to choose that, and move on to the next question.&lt;/p&gt;

&lt;p&gt;I usually tend to group the left and right options, and add a &lt;code&gt;justify-content: space-between&lt;/code&gt;. Why is that not an option? Is that wrong? Can we talk about it?&lt;/p&gt;

&lt;h1&gt;
  
  
  Question 2
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;raiseError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;err&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;MyError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What is the return type of this function in TypeScript?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Options - Single Choice&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;MyError&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;void&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;null&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;undefined&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;never&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My thoughts
&lt;/h2&gt;

&lt;p&gt;The clock was ticking, and I chose &lt;code&gt;void&lt;/code&gt;. As that's how I would annotate this function. Makes sense, right? Think again! The return type of this function isn't &lt;code&gt;void&lt;/code&gt;, it's &lt;code&gt;never&lt;/code&gt;. As it's impossible for this function to return.&lt;/p&gt;

&lt;p&gt;To make it return &lt;code&gt;void&lt;/code&gt;, the throwing should be conditional. (wrapped with an &lt;code&gt;if&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;I believe this question is wrong because it doesn't say much about your TypeScript experience. I mean, how many dedicated throw functions do you have in your codebase? Most functions are constructed in a way that they have a return path. Either with a value, undefined, or void.&lt;/p&gt;

&lt;p&gt;And when you do come across this edge case in your day job, how hard would it be to place your cursor at the function, and wait for that pretty tool-tip to appear, telling you the exact return type?&lt;/p&gt;

&lt;h1&gt;
  
  
  Question 3
&lt;/h1&gt;

&lt;p&gt;Which HTTP methods are idempotent?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Options - Multiple Choice&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All of them are idempotent as it is a stateless protocol&lt;/li&gt;
&lt;li&gt;None of the HTTP methods are idempotent.&lt;/li&gt;
&lt;li&gt;All of them except for POST, CONNECT and sometimes PATCH.&lt;/li&gt;
&lt;li&gt;All of them except for POST, OPTIONS and TRACE.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My thoughts
&lt;/h2&gt;

&lt;p&gt;First of all, this was a test for a &lt;code&gt;frontend developer&lt;/code&gt; position. Are frontend developers really expected to know if certain HTTP methods are idempotent or not? Isn't that for the API developers to know? I really did not know the answer to this question.&lt;/p&gt;

&lt;p&gt;After the test, I found out that the HTTP spec does have this specified, while I assumed that it was for the API spec (like &lt;code&gt;open-api&lt;/code&gt;) to decide.&lt;/p&gt;

&lt;p&gt;Anyways, I guessed that &lt;code&gt;all of them&lt;/code&gt; are idempotent, as HTTP doesn't hold state. My database does. But according to &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Idempotent#technical_knowledge" rel="noopener noreferrer"&gt;MDN&lt;/a&gt;, it should have been &lt;code&gt;all of them except for POST, OPTIONS, and TRACE&lt;/code&gt;. Today I learned.&lt;/p&gt;

&lt;p&gt;Now the question is, what if I make my &lt;code&gt;POST&lt;/code&gt; handler &lt;code&gt;idempotent&lt;/code&gt;? Doesn't this question depend a tiny bit on the API that we're talking about?&lt;/p&gt;

&lt;h1&gt;
  
  
  Question 4
&lt;/h1&gt;

&lt;p&gt;Which of the following browser actions/events are triggered by changing the CSS property &lt;code&gt;opacity&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Options - Multiple Choice&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Layout operations performed&lt;/li&gt;
&lt;li&gt;Painting/Rasterizing&lt;/li&gt;
&lt;li&gt;Page composited together&lt;/li&gt;
&lt;li&gt;None of the above&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My thoughts
&lt;/h2&gt;

&lt;p&gt;Seriously? For what would we need this? I guess that the browser does a &lt;code&gt;repaint&lt;/code&gt;, so that excludes the last option. &lt;code&gt;Opacity&lt;/code&gt; doesn't change the layout, so there wouldn't be any &lt;code&gt;layout operations&lt;/code&gt;. But what about &lt;code&gt;Page composited together&lt;/code&gt;? I don't know. I really don't. Does that make me a bad developer? &lt;/p&gt;

&lt;p&gt;Let's move on. Do you still want that opacity on your navbar?&lt;/p&gt;

&lt;h1&gt;
  
  
  Trick Questions
&lt;/h1&gt;

&lt;p&gt;There were six more weird technical questions that made more or less sense than the four above. But they all had one thing in common. It felt like they were trying to trick me, and it are questions that I don't need to know to be able to develop solid applications.&lt;/p&gt;

&lt;p&gt;When I would need to know it, I'm able to open my browser, and find the right answer in a matter of minutes. I'm a developer, but I suck at trivia.&lt;/p&gt;

&lt;p&gt;Two more questions to wrap this up? Remember... your time is ticking:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question 11&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;How would you explain a complicated technical problem to a colleague having none to very little technical understanding?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Write answer here...&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Question 12&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;How would you go about getting a buy-in for your project from multiple stakeholders at work?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Write answer here...&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I don't know what you want me to say. Do you have more details? Can I get another coffee and 30 minutes of your time? Let's talk about it.&lt;/p&gt;

&lt;h1&gt;
  
  
  My score
&lt;/h1&gt;

&lt;p&gt;The "nice" thing about this automated test, is that you'll get your score right away. I had a total score of 47%. I suck at React, HTTP, Communication, well, basically in every area.&lt;/p&gt;

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

&lt;p&gt;As expected, a few hours after my submission, I got the following mail. A little surprised that that part wasn't automated as well.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hi Stephan,&lt;/p&gt;

&lt;p&gt;Thanks for doing the skills test! It was a tough decision as we've had so many great applications, but we've decided to move forward with other candidates. &lt;strong&gt;Really looking for folks who've got more experience with javascript.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well, this really was a motivational experience &lt;em&gt;(not)&lt;/em&gt;. Do you know the saying &lt;em&gt;"You've dodged a bullet?"&lt;/em&gt;. That's how I came to think about this kind of interview tests. &lt;/p&gt;

&lt;p&gt;As I also operate on the hiring side, this provides me valuable insights into how the industry works. But if any recruiting person is reading this, please stop it. You're hiring developers specialized in interviews. Not in creating awesome software.&lt;/p&gt;

</description>
      <category>career</category>
      <category>personal</category>
    </item>
    <item>
      <title>A quick dive into generators</title>
      <dc:creator>Stephan Meijer</dc:creator>
      <pubDate>Thu, 15 Apr 2021 07:36:25 +0000</pubDate>
      <link>https://dev.to/smeijer/a-quick-dive-into-generators-o0l</link>
      <guid>https://dev.to/smeijer/a-quick-dive-into-generators-o0l</guid>
      <description>&lt;p&gt;I've briefly mentioned generators earlier in my &lt;a href="https://dev.to/smeijer/three-ways-to-handle-recursion-5g2l"&gt;article about recursion&lt;/a&gt;. Today, I'm going to explain the concept of generators to you, and why I believe that they are an important thing to know. If you haven't read that article, I'd recommend doing so, as this explanation builds upon that one.&lt;/p&gt;

&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Let's take the recursive function and the recursive generator function from the earlier article. Both these functions convert a tree-like structure to a flat list where each item has an &lt;code&gt;id&lt;/code&gt; and a &lt;code&gt;parent&lt;/code&gt; property:&lt;/p&gt;

&lt;p&gt;The recursive function looked like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;FlatNode&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FlatNode&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="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}];&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&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;child&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;nodes&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;While it's generator variant looked like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Generator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;FlatNode&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;yield&lt;/span&gt; &lt;span class="p"&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;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&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;child&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;yield&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;node&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;Now, most of my projects have an utility that I named &lt;code&gt;ensureArray&lt;/code&gt;. It's a nifty little helper that wraps values in an array, unless it already is an array. Something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;ensureArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;object&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="nx"&gt;object&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;I share this because this little utility lets me clean up these functions and make the similarities more obvious. I'll also stop annotating the examples with types, to further reduce the noise.&lt;/p&gt;

&lt;h1&gt;
  
  
  Recursive generators
&lt;/h1&gt;

&lt;p&gt;In case you've never seen generators before, (overly simplified), generators are functions decorated with an &lt;code&gt;*&lt;/code&gt; and using the &lt;code&gt;yield&lt;/code&gt; keyword to return values. There is a lot to read about them, but the nice thing is that they are executed lazily. Meaning, when we call &lt;code&gt;flatten&lt;/code&gt; here, it's possible to only process the first &lt;code&gt;n&lt;/code&gt; nodes, and ignore the rest. Where the non-generator variant would first process the entire tree, only to discard everything afterward, generators allow us to only process the absolute minimum of what's required for the task at hand. &lt;/p&gt;

&lt;p&gt;We'll come back to that. Let's take a look at the implementation first. I've simplified the examples from above using the &lt;code&gt;ensureArray&lt;/code&gt; helper, and I've added a log statement:&lt;/p&gt;

&lt;p&gt;Recursive function:&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;function&lt;/span&gt; &lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flatten&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&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;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}];&lt;/span&gt;

  &lt;span class="k"&gt;for&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;child&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;ensureArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;nodes&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;Recursive generator:&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;function&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flatten&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&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;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;for&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;child&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;ensureArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;node&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;You see the similarities, right? I hope that makes it less daunting.&lt;/p&gt;

&lt;p&gt;Instead of adding the node to an array, we directly &lt;code&gt;yield&lt;/code&gt; (return) it, and instead of pushing nested nodes to that same array, we also &lt;code&gt;yield&lt;/code&gt; those. The &lt;code&gt;*&lt;/code&gt; that you'll see behind that second yield, is syntactic sugar to &lt;code&gt;yield&lt;/code&gt; all results in an array/iterator individually.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;yield&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;could just as well be written as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for&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;result&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Lazy evaluation
&lt;/h1&gt;

&lt;p&gt;So the thing I mentioned earlier about the lazy behavior? Imagine we need to do something only for the first three nodes in that tree. We would write something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;idx&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;handle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the traditional, non-generator approach, this would result in the following log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatten 1
flatten 2
flatten 3
flatten 4
flatten 5
flatten 6
flatten 7
flatten 8
flatten 9
flatten 10
flatten 11
handle 1
handle 2
handle 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That log tells us that the entire tree is processed and converted to the flat array before we can handle the 3 nodes that we need. The processing time that we used for those other 8 nodes, is wasted.&lt;/p&gt;

&lt;p&gt;Now, if we'd do the same with that generator function, we'd need to change the syntax a bit:&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;nodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;idx&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;handle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We no longer use the &lt;code&gt;idx&lt;/code&gt; property, but instead, call the &lt;code&gt;next&lt;/code&gt; function from the &lt;code&gt;nodes&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;flatten&lt;/code&gt; call itself doesn't do much there. It does not invoke the &lt;code&gt;flatten&lt;/code&gt; function. The log on that first line? It's not printed. Instead, the call prepares the generator and returns an object with a &lt;code&gt;next&lt;/code&gt; method. When we call the &lt;code&gt;next&lt;/code&gt; method, the generator will run till the next &lt;code&gt;yield&lt;/code&gt; inside that function. When it meets that &lt;code&gt;yield&lt;/code&gt;, it will return the value that's being yielded.&lt;/p&gt;

&lt;p&gt;The return value of &lt;code&gt;next&lt;/code&gt; is not just that yielded value. It's an object with a &lt;code&gt;value&lt;/code&gt; prop, holding your yielded value, and a &lt;code&gt;done&lt;/code&gt; property, holding a boolean that will tell you if this generator is done generating values.&lt;/p&gt;

&lt;p&gt;So the output from that last loop?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flatten 1
handle 1
flatten 2
handle 2
flatten 3
handle 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's important to understand that the output order has changed. We can handle the node, as soon as the generator yields one. It doesn't yield all nodes at once, it yields every node individually, as soon as it has it. We don't need to wait for the entire tree to be processed. In fact, the processing won't continue, until we explicitly ask for the next node.&lt;/p&gt;

&lt;p&gt;Once we've handled our three nodes, we stop our loop, and the tree is not further processed. We haven't wasted any processing time using the generator approach.&lt;/p&gt;

&lt;p&gt;You probably don't always need loops, and sometimes you do want to process all or nothing. In those cases, it's trivial to wrap the call in &lt;code&gt;Array.from&lt;/code&gt;, to get all nodes at once. Just like you would have with the non-generator approach:&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;nodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// [{ id:  … }]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've used a simple loop in this example, but you can imagine that this is quite powerful. Without changes to the generator itself, it can be wrapped with logic to only handle the first &lt;code&gt;n&lt;/code&gt; results, or only process until a certain condition is met.&lt;/p&gt;

&lt;p&gt;Also, isn't it just beautiful, how easy it is to write recursive functions this way? No intermediate arrays. No return complexity. Recursive tree parsing, in 3 lines. All it asks is to get familiar with &lt;code&gt;yield&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&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;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;for&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;child&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;ensureArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Final word
&lt;/h1&gt;

&lt;p&gt;Generators might look a bit scary at first, but they come with a lot of flexibility and power. I can imagine that they look daunting, especially for inexperienced developers. But I would really recommend getting familiar with them. They make a great asset to your utility belt.&lt;/p&gt;

&lt;p&gt;If you have questions related to this subject, please let me know in the comments. I'm happy to explain things in more detail.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;👋 I'm Stephan, and I'm building &lt;a href="http://rake.red"&gt;rake.red&lt;/a&gt;. If you wish to read more of mine, follow me on &lt;a href="https://twitter.com/meijer_s"&gt;Twitter&lt;/a&gt; or check my work at &lt;a href="https://meijer.ws"&gt;meijer.ws&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Three ways to handle recursion</title>
      <dc:creator>Stephan Meijer</dc:creator>
      <pubDate>Mon, 12 Apr 2021 12:58:24 +0000</pubDate>
      <link>https://dev.to/smeijer/three-ways-to-handle-recursion-5g2l</link>
      <guid>https://dev.to/smeijer/three-ways-to-handle-recursion-5g2l</guid>
      <description>&lt;p&gt;This is a follow-up post on &lt;a href="https://dev.to/smeijer/you-might-not-need-recursion-282b"&gt;You might not need recursion&lt;/a&gt;. In this article, I'm going to show you three different ways to convert a Tree data structure, to a flat list while maintaining a reference to the parent.&lt;/p&gt;

&lt;p&gt;Let's start with the input that we're working with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, this tree has a hierarchical structure. Every node has an &lt;code&gt;id&lt;/code&gt;, and an optional property called &lt;code&gt;children&lt;/code&gt; which is either an &lt;code&gt;array&lt;/code&gt; or an &lt;code&gt;object&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;We're going to convert this to a flat array holding items with an &lt;code&gt;id&lt;/code&gt; and a &lt;code&gt;parent&lt;/code&gt; property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;FlatNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FlatNode&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;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="err"&gt;…&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Recursive function
&lt;/h1&gt;

&lt;p&gt;When working with Tree-like structures like the one above, we tend to write recursive functions by default. Despite the fact that recursion is hard to grasp for a lot of us. Even amongst senior developers, with many years of experience.&lt;/p&gt;

&lt;p&gt;When we write a recursive function to handle this, we end up with something like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;FlatNode&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FlatNode&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="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}];&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&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;child&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When calling &lt;code&gt;flatten(tree)&lt;/code&gt;, it starts processing at the root node and recursively walks down the tree walking over the children, to return them as a &lt;code&gt;FlatNode&lt;/code&gt;. To be able to keep the reference to the parent, we need to pass in the parent as an additional function argument.&lt;/p&gt;

&lt;p&gt;There is nothing wrong with this function. And I believe that it's perfectly understandable. However, my experience also tells me that I will have coworkers working on the same code base, that find this concept hard to understand.&lt;/p&gt;

&lt;p&gt;If you haven't worked with recursion before, and think you'll understand what's going on, I want to challenge you. Take the &lt;code&gt;tree&lt;/code&gt; object from above, and write this &lt;code&gt;flatten&lt;/code&gt; function without looking back to my example before you have a working result.&lt;/p&gt;

&lt;h1&gt;
  
  
  Flat iteration
&lt;/h1&gt;

&lt;p&gt;This recursive function can also be rewritten to a flat loop. The following example has the same input and output as the recursive function, but all operations take place in a single call frame. There is no recursion and there are no calls to an external function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rootNode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;FlatNode&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FlatNode&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;rootNode&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="k"&gt;while&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="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;for&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;child&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;)&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="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&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;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;nodes&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, I do believe that this is easier to follow for people unfamiliar with recursion. But I also think that the difference in complexity is fading. This is a more complex function than the one from &lt;a href="https://dev.to/smeijer/you-might-not-need-recursion-282b#iterative"&gt;my earlier article&lt;/a&gt; because the subject is more advanced as well.&lt;/p&gt;

&lt;p&gt;From the performance point of view, in Chrome the recursive function is twice as fast, while in Firefox the non-recursive function is the faster one.&lt;/p&gt;

&lt;p&gt;Also, mind that while the output has the same structure, the resulting nodes are in a different order. The recursive function eagerly moves to the child nodes and handles children before siblings. While the loop handles siblings before children. Making both functions merge their results in a different order.&lt;/p&gt;

&lt;h1&gt;
  
  
  Recursive generators
&lt;/h1&gt;

&lt;p&gt;Generators are particularly well suited to tackle recursive problems.&lt;/p&gt;

&lt;p&gt;In case you've never seen generators before, (overly simplified), generators are functions decorated with an &lt;code&gt;*&lt;/code&gt; and using the &lt;code&gt;yield&lt;/code&gt; keyword to return values. &lt;/p&gt;

&lt;p&gt;Let's take a look at the implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Generator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;FlatNode&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;yield&lt;/span&gt; &lt;span class="p"&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;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&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;child&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;yield&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;node&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;Now, this solution will return the values in the same order as the recursive function. In fact, they do look quite similar, except that we don't need that temporary &lt;code&gt;nodes&lt;/code&gt; array to merge the results.&lt;/p&gt;

&lt;p&gt;Instead of adding the node to an array, we directly &lt;code&gt;yield&lt;/code&gt; (return) it, and instead of pushing nested nodes to the same array, we also &lt;code&gt;yield&lt;/code&gt; those. &lt;/p&gt;

&lt;h1&gt;
  
  
  Final word
&lt;/h1&gt;

&lt;p&gt;Whatever you prefer is fine. I think it's most important to choose the method that's most familiar to your team and most fitting to your requirements. Remember that for inexperienced developers the loop is easier to understand and that it's always the easiest one to debug.&lt;/p&gt;

&lt;p&gt;I personally would recommend getting familiar with generators. They look a bit scary at first, but they come with a lot of flexibility and power.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;👋 I'm Stephan, and I'm building &lt;a href="http://rake.red"&gt;rake.red&lt;/a&gt;. If you wish to read more of mine, follow me on &lt;a href="https://twitter.com/meijer_s"&gt;Twitter&lt;/a&gt; or check my work at &lt;a href="https://meijer.ws"&gt;meijer.ws&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

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