<?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: Zach Olivare</title>
    <description>The latest articles on DEV Community by Zach Olivare (@olivare).</description>
    <link>https://dev.to/olivare</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%2F309980%2F5cf43724-23b5-4774-8482-e72d79c37d53.jpeg</url>
      <title>DEV Community: Zach Olivare</title>
      <link>https://dev.to/olivare</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/olivare"/>
    <language>en</language>
    <item>
      <title>How to make your app faster with React's key prop</title>
      <dc:creator>Zach Olivare</dc:creator>
      <pubDate>Tue, 11 Apr 2023 17:28:19 +0000</pubDate>
      <link>https://dev.to/olivare/how-to-make-your-app-faster-with-reacts-key-prop-170n</link>
      <guid>https://dev.to/olivare/how-to-make-your-app-faster-with-reacts-key-prop-170n</guid>
      <description>&lt;p&gt;Every React element has a special &lt;code&gt;key&lt;/code&gt; prop. It has a single purpose: &lt;strong&gt;to uniquely identify a React element&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There are two circumstances in which you as a React developer can use the &lt;code&gt;key&lt;/code&gt; prop to your advantage:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;to prevent an element from being unnecessarily re-rendered &lt;em&gt;(the most common)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;to force an element to be re-rendered &lt;em&gt;(advanced, used only in rare circumstances)&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Preventing unnecessary re-renders
&lt;/h2&gt;

&lt;p&gt;Far and away the most common and useful purpose of the &lt;code&gt;key&lt;/code&gt; prop is to prevent unnecessary re-renders of a component that is created by mapping over an array.&lt;/p&gt;

&lt;p&gt;Using it usually looks something like this:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;MyComponent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;items&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;ul&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;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&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;item&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;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&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;👉 &lt;em&gt;But how does using &lt;code&gt;key&lt;/code&gt; prevent unnecessary re-renders?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;key&lt;/code&gt; prevents unnecessary re-renders by allowing React to determine that an element still exists, unchanged from the last render, it just may have moved.&lt;/p&gt;

&lt;p&gt;👉 &lt;em&gt;How does that work?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Remember that key's only purpose is &lt;strong&gt;to uniquely identify a React element&lt;/strong&gt;. Every time each one of those &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; elements is rendered, React will do a "diff" (calculate the "difference") between the previous element that was rendered with a particular &lt;code&gt;key&lt;/code&gt; and and current element that is being rendered with that key. If there is no difference between the two, React will not modify that element in the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model"&gt;DOM&lt;/a&gt;. If it does find a difference, React will unmount the previous component instance, create a new component instance, and mount that one instead.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;key&lt;/code&gt; prop won't/can't/shouldn't do anything to prevent a component from re-rendering if it has changed. But it can &amp;amp; should prevent a component from re-rendering if it has only moved!&lt;/p&gt;

&lt;p&gt;👉 &lt;em&gt;Can you give a more concrete example?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What happens if you reverse the &lt;code&gt;items&lt;/code&gt; array from the above snippet? Well, with our current code, React wouldn't create or destroy a single &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; element! It would simply re-arrange the existing &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt;s to match the new order of the array. And how does React know where to place each element? Because of it's unique &lt;code&gt;key&lt;/code&gt; of course!&lt;/p&gt;

&lt;p&gt;Moving existing DOM nodes is &lt;em&gt;much&lt;/em&gt; more performant than destroying and re-creating nodes.&lt;/p&gt;

&lt;h3&gt;
  
  
  The wrong way -- using index as the key
&lt;/h3&gt;

&lt;p&gt;Let's contrast our previous implementation to a very common, yet incorrect one:&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&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;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&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="cm"&gt;/* This is bad, don't set key to index */&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;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="si"&gt;}&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;item&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;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see this &lt;code&gt;key={index}&lt;/code&gt; in tutorials and production code alike, but its usage is lazy and has negative performance implications.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you think it's your only solution, scroll down to the last section of this blog post. I promise there's a better way!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The catch is though, that oftentimes those performance implications crop up later, well after the &lt;code&gt;key={index}&lt;/code&gt; code was initially written.&lt;/p&gt;

&lt;p&gt;👉 &lt;em&gt;But why is &lt;code&gt;key={index}&lt;/code&gt; bad?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If the contents of the array that you're mapping over is guaranteed never to change in any way, then there wouldn't be a performance hit caused by setting key to index.&lt;/p&gt;

&lt;p&gt;But in my experience, &lt;strong&gt;there is almost no array (or code in general) that is guaranteed never to change in any way&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Sure, the code you're writing today has no way for the array to change. But what happens next month when product wants to...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add sort functionality to the UI?&lt;/li&gt;
&lt;li&gt;allow the user to add items to the list?&lt;/li&gt;
&lt;li&gt;delete items?&lt;/li&gt;
&lt;li&gt;drag and drop to re-order items?&lt;/li&gt;
&lt;li&gt;filter the list?&lt;/li&gt;
&lt;li&gt;search for items in the list?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Every one of those features will be negatively impacted by&lt;/strong&gt; &lt;code&gt;key={index}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The developer adding that new feature might be a different member of your team, and they might never have to touch the file that you wrote &lt;code&gt;key={index}&lt;/code&gt; in, so they don't know about it. They just trusted you to do the right thing and you let them down 💔.&lt;/p&gt;

&lt;p&gt;👉 &lt;em&gt;Why is there a negative performance impact to &lt;code&gt;key={index}&lt;/code&gt;?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Consider the scenario where one single item is inserted to the middle of the array.&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="c1"&gt;// before&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Item 0&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&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;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Item 1&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&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;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Item 2&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// after&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Item 0&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&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;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;---NEW ITEM---&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&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;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Item 1&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;// notice this element's key has changed&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Item 2&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;// notice this element's key has changed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;key&lt;/code&gt; being set to index, the key of every element after the added element will change. So even though none of those elements have actually changed, they will all be unmounted and have duplicate elements re-created and re-mounted in their place, a process that takes infinitely longer than just leaving the DOM nodes alone and adding the new element ot the middle.&lt;/p&gt;

&lt;p&gt;This is equally true for sorting, filtering, searching, and every other type of operation listed in the bullets above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Force an element to re-render
&lt;/h2&gt;

&lt;p&gt;We've gathered by now that when an React element's &lt;code&gt;key&lt;/code&gt; changes, it is unmounted and a brand new component instance is created in its place.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I say "re-render" in the section title, but that is overly simplistic. Changing an element's &lt;code&gt;key&lt;/code&gt; will not cause the same component instance to re-render. It will cause the existing component instance to be unmounted and eventually garbage collected. It then causes a brand new component instance to be created, and mounted.&lt;/p&gt;

&lt;p&gt;That process of creating a new component instance is definitely slower than a standard re-render, so again, use this pattern of "forceful re-rendering" sparingly and only when there are no better options.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We've also seen that far and away the most common place to use &lt;code&gt;key&lt;/code&gt; is inside of a &lt;code&gt;.map()&lt;/code&gt; function. But it doesn't have to be inside of a map. &lt;code&gt;key&lt;/code&gt; can exist on any React element.&lt;/p&gt;

&lt;p&gt;So if you need to force a component to "re-render" in a situation that it otherwise wouldn't one way to approach that problem is to force a change to its &lt;code&gt;key&lt;/code&gt;.&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="c1"&gt;// WARNING: This is shitty code and for illustrative purposes only&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;MyComponent&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;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setKey&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&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="c1"&gt;// Every time onClick() fires, OtherComponent will be replaced&lt;/span&gt;
    &lt;span class="c1"&gt;// with a completely new component instance, resetting any and&lt;/span&gt;
    &lt;span class="c1"&gt;// all state it and any of its children maintain.&lt;/span&gt;

    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;OtherComponent&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What makes key so special?
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;key&lt;/code&gt; prop is special because it is one of only two props that are not accessible from within the component instance (the other being &lt;code&gt;ref&lt;/code&gt;).&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MyComponent&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;MyComponent&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="c1"&gt;// props.key is undefined&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In fact, if you attempt to access &lt;code&gt;key&lt;/code&gt; from within a component instance, you'll get a warning in your console:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ohmBAyCU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dgbjgtmvpor1dqmf2l4y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ohmBAyCU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dgbjgtmvpor1dqmf2l4y.png" alt="Warning: key is not a prop. Trying to access it will result in  raw `undefined` endraw  being returned. If you need to access the same value within the child component, you should pass it as a different prop." width="736" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  But I don't have an id for this object 😭
&lt;/h2&gt;

&lt;p&gt;Inevitably, you'll run into a situation where you need to render a list of objects, but those objects don't come with prepackaged a unique identifier. Maybe they're coming from a third-party API, or maybe they're just a list of strings.&lt;/p&gt;

&lt;p&gt;In cases like that, there are still a few options:&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 1: Generate a random id for each object
&lt;/h3&gt;

&lt;p&gt;If you're fetching a list of objects from an API and they don't have a unique identifier, you can just add one yourself.&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getMovies&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;response&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://example.com/movies.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;movies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="c1"&gt;// Add a unique id to each movie with Math.random()&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;movies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({...&lt;/span&gt;&lt;span class="nx"&gt;movie&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="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&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 just like that, you have a unique identifier for each item!&lt;/p&gt;

&lt;p&gt;You can of course use library like &lt;a href="//npmjs.com/package/nanoid"&gt;nanoid&lt;/a&gt; or &lt;a href="https://www.npmjs.com/package/uuidv4"&gt;uuid&lt;/a&gt; to generate a unique identifier, but I've found that &lt;code&gt;Math.random()&lt;/code&gt; is good enough for most use cases.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; It's important that we're doing this process of assigning random ids &lt;em&gt;before&lt;/em&gt; we return the array of movies. If we were to do it inside of the &lt;code&gt;.map()&lt;/code&gt; function in the component, we'd be generating a new id every time the component re-renders, which would defeat the purpose of having a unique id in the first place.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Option 2: Combine multiple properties into a key
&lt;/h3&gt;

&lt;p&gt;If none of your object's properties are guaranteed to be unique, but combined they are almost certain to be unique, that is usually good enough.&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getUsers&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;response&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://example.com/users.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="c1"&gt;// Add a psuedo-unique id by combining multiple properties&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({...&lt;/span&gt;&lt;span class="nx"&gt;user&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&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;lastName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>I quit LastPass and moved to 1Password after 10 years</title>
      <dc:creator>Zach Olivare</dc:creator>
      <pubDate>Mon, 06 Mar 2023 14:12:00 +0000</pubDate>
      <link>https://dev.to/olivare/i-quit-lastpass-and-moved-to-1password-after-10-years-51c9</link>
      <guid>https://dev.to/olivare/i-quit-lastpass-and-moved-to-1password-after-10-years-51c9</guid>
      <description>&lt;p&gt;I've been a big advocate for LastPass for the last 10 years. I have encouraged probably around a dozen other people to use it. I even called it out specifically (along with 1password) in my &lt;a href="https://www.olivare.net/blog/password-manager"&gt;Use a password manager&lt;/a&gt; post back in 2020.&lt;/p&gt;

&lt;p&gt;But I've been growing increasingly frustrated with LastPass recently so I decided to try out 1Password. &lt;em&gt;tldr&lt;/em&gt; I found 1Password to solve many of the issues that I have with LastPass; and even though it's not perfect, overall I think 1Password is a better product.&lt;/p&gt;

&lt;p&gt;Please allow me to explain why:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. LastPass had a major, concerning, data-compromising Hack in 2022
&lt;/h2&gt;

&lt;p&gt;There are a lot of &lt;a href="https://grahamcluley.com/lostpass-after-the-lastpass-hack-heres-what-you-need-to-know/"&gt;details to this hack&lt;/a&gt;. But my cliff note version is that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;URLs and usernames have all been stolen in cleartext (not encrypted)&lt;/li&gt;
&lt;li&gt;Passwords and secure notes have also been stolen, but the information was encrypted.&lt;/li&gt;
&lt;li&gt;"Unencrypting" one of those passwords would cost the attacker somewhere between $75,000 and $1.5M&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So unless you're super famous or work for the government, your passwords at least are reasonably safe. But the attackers now know which sites you use, which would make it easier to perform a phishing attack on you.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. 1Password is more secure
&lt;/h2&gt;

&lt;p&gt;1Password has a unique security concept they call a &lt;a href="https://support.1password.com/secret-key-security"&gt;Secret Key&lt;/a&gt;. You can read this &lt;a href="https://blog.1password.com/what-the-secret-key-does"&gt;in depth explanation&lt;/a&gt; to how the secret key works, but again, here are my cliff notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's a 128-bit (&lt;em&gt;i.e.&lt;/em&gt; really long) alpha numeric code&lt;/li&gt;
&lt;li&gt;It's combined with your master password to encrypt your data&lt;/li&gt;
&lt;li&gt;It makes it so that even if 1Password was &lt;em&gt;hacked in exactly the same way as LastPass was in 2022&lt;/em&gt;, the hacker would not be able to crack a single password &lt;a href="https://blog.1password.com/why-we-moved-to-256-bit-aes-keys/#how-long-is-long"&gt;even if he put every computer on Earth to work on the cracking and ran them for zillions of times the age of the universe&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;1Password also encrypts your item URLs and titles. So even if 1Password were breached, the hacker would have no valuable information about you.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. 1Password is a lot easier to use
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3a. 1Password has an Archive
&lt;/h3&gt;

&lt;p&gt;When you use a password manager for long enough, you undoubtedly have some passwords that gather dust. Passwords that you think you'll probably never need again; maybe an old job, or maybe you accidentally (or on purpose) created two accounts for the same website.&lt;/p&gt;

&lt;p&gt;These are passwords that you don't want to appear in normal searches, and passwords that you don't want suggested to you to sign into stuff. But at the same time, &lt;strong&gt;deleting them is scary&lt;/strong&gt;! It's hard to be certain that you'll &lt;strong&gt;never&lt;/strong&gt; need these passwords for any reason ever again.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I made the mistake of assuming this once when I left a job. I deleted every "old" password related to that job, and thought I was fine. That was until I needed to log into my old HR platform to access tax documents 6 months later.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;1Password's Archive gives you an easy place to toss these "old" passwords, with the confidence that if you do ever need them again for any reason, they'll still be there for you to go and find.&lt;/p&gt;

&lt;h3&gt;
  
  
  3b. 1Password makes sharing passwords with family a lot easier
&lt;/h3&gt;

&lt;p&gt;LastPass conflates the concepts of item organization and item sharing. With a LastPass family subscription, the way you share passwords to other family members is to move the password from whatever folder you wanted it in into a special "Shared-" folder.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;So if you have a folder in LastPass where you normally put all passwords of a certain type, e.g. &lt;code&gt;Streaming&lt;/code&gt;, in order to share that password with your family you have to move it into a &lt;em&gt;different&lt;/em&gt; folder, &lt;code&gt;Shared-Streaming&lt;/code&gt;. So your streaming passwords are now split up between two different folders. That's kind of annoying.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In 1Password, you share with family members by creating different "Vaults". Conceptually, a "Vault" is similar to a "Shared-Folder"; but for me there are some meaningful differences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vaults are not the primary tool for password organization in 1Password (that's where &lt;a href="https://support.1password.com/favorites-tags"&gt;favorites and tags&lt;/a&gt; come in). A vault's primary purpose is for sharing (think of having different safes in your house and giving different people the combination to different safes).&lt;/li&gt;
&lt;li&gt;LastPass "Shared-" folders inter-mingle too easily with regular folders. Vaults are distinct; you only have a few of them and they are not intermingled with Tags.&lt;/li&gt;
&lt;li&gt;You can name vaults whatever you want, they don't have to begin with "Shared-" (the detail oriented folks out there will understand)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3c. Tags are better than Folders
&lt;/h3&gt;

&lt;p&gt;LastPass uses folders for organization, 1Password uses &lt;a href="https://support.1password.com/favorites-tags"&gt;tags&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Any users of Gmail will immediately recognize the concept of tags and why they're inherently superior to folders: an item can only be in one folder, but it can have any number of tags.&lt;/p&gt;

&lt;p&gt;By eliminating folders, 1Password also eliminated the dreaded LastPass &lt;code&gt;(none)&lt;/code&gt; folder. This is the place in your LastPass vault that all items that don't have a folder specified go. I'm pretty sure its intended purpose is strictly to shame you for having an unorganized LastPass vault.&lt;/p&gt;

&lt;p&gt;I had dozens of folders and subfolders in LastPass (&amp;gt; 50). But when I moved over to 1Password I realized that most of those folders served no purpose whatsoever. Or said another way, looking at those items together as a group provided no benefit. The only reason the folders existed was to get them out of &lt;code&gt;(none)&lt;/code&gt;. I currently have 9 1Password tags.&lt;/p&gt;

&lt;h3&gt;
  
  
  3d. 1Password has built in "2FA codes"
&lt;/h3&gt;

&lt;p&gt;You know your Authenticator app, right? The app on your phone that generates Time-based One Time Passwords (TOTP). You might use &lt;a href="https://apps.apple.com/us/app/google-authenticator/id388497605"&gt;Google Authenticator&lt;/a&gt; or &lt;a href="https://authy.com/"&gt;Authy&lt;/a&gt;, or even &lt;a href="https://apps.apple.com/us/app/lastpass-authenticator/id1079110004"&gt;LastPass Authenticator&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;1Password (mostly) &lt;a href="https://support.1password.com/one-time-passwords"&gt;replaces&lt;/a&gt; the need for that app. When you sign into an app that has 2FA configured in 1Password, the 2FA code will automatically be copied to your clipboard, so that without a single extra click or even picking up your phone, you can just paste the code right into the site and go.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Isn't not involving your cell phone (a 2nd device) less secure?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Short answer: No. If you keep passwords on the same phone that you have your Authenticator app on (which most everyone does) then you don't actually have a "2nd factor". You're better off spending your time focusing on keeping your 1Password master password long and secure.&lt;/p&gt;

&lt;p&gt;1Password's head of security has a much more in-depth write up on that topic &lt;a href="https://blog.1password.com/totp-for-1password-users"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pro tips for using 1Password as your Authenticator app:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The "Scan QR code" button seems to be broken on the Mac app currently; use the one in the browser extension instead&lt;/li&gt;
&lt;li&gt;If the site doesn't give you a QR code, and instead only the "copy this code into your app" function, 1Password supports that too, it's just not as obvious.

&lt;ul&gt;
&lt;li&gt;Choose &lt;b&gt;Edit&lt;/b&gt; on the password you want to save a OTP code for&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;add another field&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;One-Time Password&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Paste the code you copied from the website&lt;/li&gt;
&lt;li&gt;Done! That process is equivalent to scanning the QR code.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3e. 1Password has a native apps for your computer instead of just a browser extension
&lt;/h3&gt;

&lt;p&gt;Using a native app to organize your saved passwords is just a better experience. It also lets 1Password use features like Touch ID and Apple Watch integrations that only native Mac apps have access to!&lt;/p&gt;

&lt;h3&gt;
  
  
  3f. 1Password supports saving multiple different websites for the same credentials
&lt;/h3&gt;

&lt;p&gt;I've encountered a number of websites over the years that have let you log in at a couple different urls. Or sometimes you need to log into multiple different subsystems of an app that share the same credentials (like corporate SSO for example).&lt;/p&gt;

&lt;p&gt;LastPass makes that a real pain to deal with. You have to duplicate the login credentials over multiple LastPass entries, and then LastPass will yell at you for having "duplicate passwords" unless you dig deep into the settings and configure "Equivalent Domains". What a nuisance.&lt;/p&gt;

&lt;p&gt;In 1Password, you can just &lt;strong&gt;add another website&lt;/strong&gt; when editing an existing entry. Couldn't be simpler.&lt;/p&gt;

&lt;h3&gt;
  
  
  3g. 1Password has first class support for multiple accounts
&lt;/h3&gt;

&lt;p&gt;If you use LastPass personally, and your workplace uses LastPass to store shared credentials, you're shit out of luck. You cannot sign into multiple LastPass accounts at the same time.&lt;/p&gt;

&lt;p&gt;The only solution is to either &lt;em&gt;never&lt;/em&gt; access personal passwords in your work browser, or copy personal/work passwords to the other vault.&lt;/p&gt;

&lt;p&gt;1Password lets you sign into multiple accounts at the same time and easily switch between them.&lt;/p&gt;

&lt;h3&gt;
  
  
  3h. 1Password has customizable fields on every entry
&lt;/h3&gt;

&lt;p&gt;A LastPass entry has a set number of fields depending on its type. A password type entry will have a name, url, username, password, and notes. And that's it. If you want to save any extra information about that site it has to go in the notes field.&lt;/p&gt;

&lt;p&gt;1Password entries feel kind of like a "phone contacts" application in that you can add any number of different types of fields to the entry.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Things LastPass does better
&lt;/h2&gt;

&lt;p&gt;I have kind of torn LastPass to shreds in this article, and I think rightly so. But there are a couple things that I think they do better than 1Password; features I would like to see 1Password adopt:&lt;/p&gt;

&lt;h3&gt;
  
  
  4a. LastPass lets you search on the Notes field
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Notes&lt;/strong&gt; field is a catch all for credential entries. You can put any helpful information in notes for that entry. One common thing I like to include in notes are some aliases to help me find this entry later.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For example the HR software that my company uses is called DayForce. But I can't remember that name for the life of me. So I always search for one of the aliases that I entered in the notes field ("SkySlope HR") and it pops right up in LastPass.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But 1Password &lt;em&gt;doesn't search on the notes fieldddddd!!!!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What the heck, come on guys.&lt;/p&gt;

&lt;h3&gt;
  
  
  4b. LastPass lets you share passwords with people outside your family subscription
&lt;/h3&gt;

&lt;p&gt;In LastPass you can share any password with any other LastPass user. Doing so frequently is annoying because the share has to be managed inside of that one password, and you have to re-enter the email every time, but nonetheless it's a nice feature to have.&lt;/p&gt;

&lt;p&gt;1Password only allows you to share passwords to one of the other people in your family or organization subscription.&lt;/p&gt;

&lt;h3&gt;
  
  
  4c. LastPass lets you save arbitrary form data
&lt;/h3&gt;

&lt;p&gt;LastPass has a "Save All Entered Data" option for saving all the data you typed into long forms. I personally use this feature for testing websites, but it's useful for any repetitive form-filling.&lt;/p&gt;

&lt;p&gt;1Password doesn't appear to have anything similar.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. A Feature both are lacking
&lt;/h2&gt;

&lt;p&gt;The ability to control which entries are suggested for auto-fill at which time, and easily toggle between them.&lt;/p&gt;

&lt;p&gt;Say for example that you only want it to suggest your work passwords when you're at work, and your home passwords when your at home. Or you have a vault/folder that contains family member accounts that you need to have access to to help them do something in once in awhile.&lt;/p&gt;

&lt;h3&gt;
  
  
  5a. LastPass has Identities
&lt;/h3&gt;

&lt;p&gt;LastPass &lt;a href="https://support.lastpass.com/help/manage-your-vault-identities-lp040001"&gt;identities&lt;/a&gt; are, in concept, exactly what I'm looking for here. Each identity has certain passwords that are only suggested while you're acting as that identity or when you're acting as the "All" identity.&lt;/p&gt;

&lt;p&gt;But it's far from perfect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The experience of moving sites between identities is frustratingly difficult&lt;/li&gt;
&lt;li&gt;The new browser plugin popup doesn't let me switch between identities (without going all the way into my vault). This feature existed before their recent re-skin of the browser plugin popup.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When adding a new site, it gets added to whatever identity you currently have active.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;That makes sense when you first think about it but is frustrating in practice. You never think about which identity is active when saving new credentials, which means that later you have to go figure out where your lost credential ended up and then move it to the correct identity (probably "personal" or the equivalent).&lt;/li&gt;
&lt;li&gt;At least let me choose the default identity that new credentials should always be added under (probably "personal") [1Password does let you choose which vault to add new credentials to by default]&lt;/li&gt;
&lt;li&gt;But preferably, let me choose which identity these credentials are added to in the creation process&lt;/li&gt;
&lt;li&gt;A configurable default would also be nice here&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5b. 1Password has nothing of the sort
&lt;/h3&gt;

&lt;p&gt;1Password does not have a way to only auto-fill credentials from a certain "active" vault.&lt;/p&gt;

&lt;p&gt;What they &lt;strong&gt;should&lt;/strong&gt; do is (this does not currently exist):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Let me right click on a vault and choose &lt;strong&gt;Make active vault&lt;/strong&gt;, and then only auto-fill credentials from that vault&lt;/li&gt;
&lt;li&gt;If I want to auto-fill from all vaults again, I would right click and &lt;strong&gt;Remove active vault&lt;/strong&gt; on that same vault I originally made "active"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Boom, done.&lt;/p&gt;

&lt;p&gt;The closest 1Password gets to something like this is the ability to &lt;a href="https://1password.community/discussion/comment/608430/#Comment_608430"&gt;Disable Vaults&lt;/a&gt;. If this setting could be toggled more quickly, it might be an acceptable solution to the problem.&lt;/p&gt;

</description>
      <category>passwordmanager</category>
      <category>1password</category>
      <category>lastpass</category>
    </item>
    <item>
      <title>How to test TypeScript type definitions with Jest</title>
      <dc:creator>Zach Olivare</dc:creator>
      <pubDate>Sun, 22 Jan 2023 23:26:21 +0000</pubDate>
      <link>https://dev.to/olivare/how-to-test-typescript-type-definitions-with-jest-1fjg</link>
      <guid>https://dev.to/olivare/how-to-test-typescript-type-definitions-with-jest-1fjg</guid>
      <description>&lt;p&gt;Have you ever toiled to get the TypeScript types just right on a particular function and thought to yourself, "I wish I could write a test to make sure that I don't break this type definition next time I edit this function"? Me too! In this post I'll give you thorough instructions on how you can do just that.&lt;/p&gt;

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

&lt;p&gt;The library we're going to use to execute these static type tests is one that I made called &lt;a href="https://github.com/zposten/jest-tsd" rel="noopener noreferrer"&gt;jest-tsd&lt;/a&gt;. It's a wrapper around &lt;a href="https://github.com/SamVerschueren/tsd" rel="noopener noreferrer"&gt;tsd&lt;/a&gt; to make it really easy to use with Jest.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install &lt;a href="https://github.com/zposten/jest-tsd" rel="noopener noreferrer"&gt;jest-tsd&lt;/a&gt; and its peer dependency.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# With yarn&lt;/span&gt;
   yarn add &lt;span class="nt"&gt;--dev&lt;/span&gt; jest-tsd @tsd/typescript
   &lt;span class="c"&gt;# Or with npm&lt;/span&gt;
   npm i &lt;span class="nt"&gt;-D&lt;/span&gt; jest-tsd @tsd/typescript
   &lt;span class="c"&gt;# Or with pnpm&lt;/span&gt;
   pnpm i &lt;span class="nt"&gt;-D&lt;/span&gt; jest-tsd @tsd/typescript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Exclude &lt;code&gt;.test-d.ts&lt;/code&gt; files from your TypeScript compiler build.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;This is important because your type definition test files will intentionally add tests that throw TS compiler errors&lt;/li&gt;
&lt;li&gt;In your &lt;code&gt;tsconfig.json&lt;/code&gt;, add:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nl"&gt;"exclude"&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="s2"&gt;"**/*.test-d.ts"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Add a new Jest test for your type definitions
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;   &lt;span class="c1"&gt;// src/init-translation.test.js&lt;/span&gt;

   &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;expectTypeTestsToPass&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;jest-tsd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

   &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should not produce static type errors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expectTypeTestsToPassAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Function Under Test
&lt;/h2&gt;

&lt;p&gt;I want to use a non-trivial example type definition in this post so that I can write some non-trivial type tests. The rest of this section is explaining how that function works, feel free to skip if you don't care.&lt;/p&gt;

&lt;p&gt;The function I've chosen here, &lt;code&gt;initTranslation&lt;/code&gt; takes 1 or 2 arguments. Its purpose is to return a &lt;code&gt;translate&lt;/code&gt; function that can be called to map a translation key to text that can be shown on a UI for a particular language or region. The optional second parameter provides a way to avoid defining the same UI string multiple times.&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="c1"&gt;// Function type overload 1&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;initTranslation&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;G&lt;/span&gt; &lt;span class="kd"&gt;extends&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;string&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="nx"&gt;genericDict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;G&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;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;G&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;

&lt;span class="c1"&gt;// Function type overload 2&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;initTranslation&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;G&lt;/span&gt; &lt;span class="kd"&gt;extends&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;string&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;P&lt;/span&gt; &lt;span class="kd"&gt;extends&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;string&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="nx"&gt;genericDict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;G&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;createPageSpecificDict&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&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="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;G&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="nx"&gt;P&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;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;G&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;P&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;

&lt;span class="c1"&gt;// Actual function implementation that is generic enough to satisfy&lt;/span&gt;
&lt;span class="c1"&gt;// the type constraints of all function type overloads.&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;initTranslation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;genericDict&lt;/span&gt;&lt;span class="p"&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;string&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;createPageSpecificDict&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&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="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="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;string&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="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="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="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* implementation */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The first argument is a dictionary object whose keys &amp;amp; values are all strings.&lt;/li&gt;
&lt;li&gt;The second argument is a function with a &lt;code&gt;link()&lt;/code&gt; param that returns a similar dictionary.

&lt;ul&gt;
&lt;li&gt;The difference is that this second object can invoke the &lt;code&gt;link()&lt;/code&gt; function as a value of the object.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;link()&lt;/code&gt; gets passed a key from the first parameter of &lt;code&gt;initTranslation&lt;/code&gt; in order to reuse that already defined UI string.&lt;/li&gt;
&lt;li&gt;In the actual implementation, &lt;code&gt;link()&lt;/code&gt; is secretly an identity function. Its only purpose is to provide strong typing of translation keys. Changing a linked key should throw a type error.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Here's a simple example of how it might be used:&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;let&lt;/span&gt; &lt;span class="nx"&gt;translate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;initTranslation&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;foo.bar&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;Foobar&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;link&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo.baz&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo.bar&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;one.two&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;1 2&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;blockquote&gt;
&lt;p&gt;Here is the full implementation of the &lt;code&gt;initTranslation&lt;/code&gt; function in case it's helpful to aid in understanding:&lt;/p&gt;


&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/init-translation.ts&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;initTranslation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;genericDict&lt;/span&gt;&lt;span class="p"&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;string&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;createPageSpecificDict&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&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="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="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;string&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="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="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="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pageSpecificDict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createPageSpecificDict&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="nx"&gt;key&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;fullDict&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;genericDict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;pageSpecificDict&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;translate&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="kr"&gt;string&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fullDict&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;??&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isLinkedKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;genericDict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;isLinkedKey&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Give access to raw dictionary if desired for unit tests&lt;/span&gt;
  &lt;span class="nx"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dictionary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fullDict&lt;/span&gt;
  &lt;span class="nx"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fullDict&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;translate&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Writing a Type Definition test
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;jest-tsd&lt;/code&gt; has a variety of &lt;a href="https://github.com/zposten/jest-tsd#assertions" rel="noopener noreferrer"&gt;different assertions available&lt;/a&gt; to you. Here we'll make use of &lt;code&gt;expectType&lt;/code&gt; and &lt;code&gt;expectError&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/init-translation.test-d.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;expectType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expectError&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;jest-tsd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;initTranslation&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;./init-translation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First a series of tests for the single parameter version of &lt;code&gt;initTranslation&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;let&lt;/span&gt; &lt;span class="nx"&gt;translate1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;initTranslation&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo.bar&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;Foobar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// It should accept key defined in first param dictionary&lt;/span&gt;
&lt;span class="nx"&gt;expectType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo.bar&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="kr"&gt;string&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;translate1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// It should throw error for key not included in dictionary&lt;/span&gt;
&lt;span class="c1"&gt;// TS Error - Argument of type '"123"' is not assignable to parameter of type '"foo.bar"'&lt;/span&gt;
&lt;span class="nf"&gt;expectError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;translate1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then similar tests for the two param version:&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;let&lt;/span&gt; &lt;span class="nx"&gt;translate2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;initTranslation&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;foo.bar&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;Foobar&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;link&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo.baz&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo.bar&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;one.two&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;1 2&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="c1"&gt;// It should accept keys defined in either first or second param dictionary&lt;/span&gt;
&lt;span class="nx"&gt;expectType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo.bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo.baz&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;one.two&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="kr"&gt;string&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;translate2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// It should throw error for key not included in either dictionary&lt;/span&gt;
&lt;span class="c1"&gt;// TS Error - Argument of type '"123"' is not assignable to parameter of type '"foo.bar" | "foo.baz" | "one.two"'&lt;/span&gt;
&lt;span class="nf"&gt;expectError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;translate2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally test to ensure that the &lt;code&gt;link()&lt;/code&gt; function will only accept keys previously defined in the first param dictionary:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Link function should only accept keys defined in first param dictionary&lt;/span&gt;
&lt;span class="nf"&gt;expectError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;initTranslation&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;foo.bar&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;Foobar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; 
  &lt;span class="c1"&gt;// TS Error - Argument of type '"one.one"' is not assignable to parameter of type '"foo.bar"'&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;link&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;one.two&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;one.one&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="c1"&gt;// Link function should not accept keys defined in second param dictionary&lt;/span&gt;
&lt;span class="nf"&gt;expectError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;initTranslation&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo.bar&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;Foobar&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;link&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;one.one&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;One&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// TS Error - Argument of type '"one.one"' is not assignable to parameter of type '"foo.bar"'&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;one.two&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;one.one&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;})),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Tests in TypeScript?
&lt;/h2&gt;

&lt;p&gt;I can hear you saying, "but Zach, you made a big deal in your &lt;a href="https://dev.to/blog/js-testing-mistakes"&gt;post on Jest testing mistakes&lt;/a&gt; saying that tests should always be written in JavaScript, not TypeScript!".&lt;/p&gt;

&lt;p&gt;And I absolutely stand by that. Writing tests in TS makes them harder to maintain over time and adds no value to the tests.&lt;/p&gt;

&lt;p&gt;The actual Jest test file described above is still written in JavaScript!&lt;/p&gt;

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

&lt;p&gt;Next time you take the time to write complicated generic types for some utility function in your shared code, I hope you'll also take a little time to write tests for those types!&lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>5 JavaScript Testing Mistakes to Avoid</title>
      <dc:creator>Zach Olivare</dc:creator>
      <pubDate>Wed, 27 Apr 2022 13:45:04 +0000</pubDate>
      <link>https://dev.to/olivare/5-javascript-testing-mistakes-to-avoid-1pjl</link>
      <guid>https://dev.to/olivare/5-javascript-testing-mistakes-to-avoid-1pjl</guid>
      <description>&lt;h2&gt;
  
  
  Mistake #1: Excessive mocking
&lt;/h2&gt;

&lt;p&gt;I've heard it said before that mocks are a code smell, but I disagree with that. Mocks are great, in moderation. It makes a lot of sense to mock out things like network calls or responses from a 3rd party library. Sometimes it also makes sense to mock your own modules in order to isolate the unit you're trying to test.&lt;/p&gt;

&lt;p&gt;But when a particular test starts requiring you to mock out multiple other modules, or when the amount of code dedicated to mocking rivals or exceeds the amount of code actually dedicated to testing in that file, something has gone wrong. Those tests have now become much, much harder to maintain than they otherwise would be.&lt;/p&gt;

&lt;p&gt;To fix this you either need to restructure your code to make it more testable, or write end-to-end tests to cover this module because it's not suitable for unit tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mistake #2: Using enzyme
&lt;/h2&gt;

&lt;p&gt;Enzyme is &lt;a href="https://dev.to/wojtekmaj/enzyme-is-dead-now-what-ekl"&gt;dead&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;tldr; it was a bumpy road for them to create a React 16 enzyme adapter, they never actually released an official React 17 adapter, and it is not possible to create a React 18 enzyme adapter without major changes to the code base that are &lt;a href="https://github.com/enzymejs/enzyme/issues/2524#issuecomment-860784387"&gt;not going to happen&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Even before Enzyme died, React Testing Library was already well on its way to becoming the community standard (it is supported out of the box with &lt;a href="https://create-react-app.dev/"&gt;Create React App&lt;/a&gt;) because unlike enzyme, Testing Library's API encourages you to avoid including component implementation details in your tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mistake #3: Snapshot testing entire components
&lt;/h2&gt;

&lt;p&gt;Snapshot tests are very alluring because they give you a lot of output while requiring you to write very little code. Jest &lt;a href="https://jestjs.io/docs/snapshot-testing"&gt;says&lt;/a&gt; that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Snapshot tests are a very useful tool whenever you want to make sure your UI does not change unexpectedly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But unfortunately, that sense of security is a lie.&lt;/p&gt;

&lt;p&gt;First and foremost, jest is wrong to say that snapshots "make sure your &lt;strong&gt;UI&lt;/strong&gt; does not change"; what they actually do is let you know when your markup changes. And it's not necessarily problematic that the markup of your component changed. There are an infinite number of changes that I can make to your markup without changing your UI at all. You know how else I can determine if your markup is going to change? By actually reading the source code.&lt;/p&gt;

&lt;p&gt;The other biggest problem with snapshots is that in real world applications they end up changing very frequently and quite dramatically. The diffs of snapshot files end up being so long that the people reviewing your code are 90% of the time going to completely ignore them, removing 100% of their value. And even when people do take the time to attempt to read your massive snapshot diff, what are they supposed to be looking for? It is an exercise in futility.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I made the title of this section about "entire components" instead of just "snapshot testing" because there is a newer way to use snapshots that I think is really neat and very effective; &lt;a href="https://jestjs.io/docs/snapshot-testing#inline-snapshots"&gt;inline snapshots&lt;/a&gt;. They are &lt;em&gt;inline&lt;/em&gt; because the snapshot is written directly into the source file instead of into a separate file. Basically it gives you the ability to write &lt;code&gt;expect().toBe()&lt;/code&gt; tests without manually entering the expected result, and also gives you the ability to easily update those expected results if your code changes.&lt;/p&gt;

&lt;p&gt;They look something like this:&lt;/p&gt;


&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;formats integer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currency&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="nx"&gt;toMatchInlineSnapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`"$1"`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toMatchInlineSnapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`"$12"`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toMatchInlineSnapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`"$123"`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;formats decimal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.12&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toMatchInlineSnapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`"$0.12"`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.123&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toMatchInlineSnapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`"$0.12"`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.129&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toMatchInlineSnapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`"$0.13"`&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;p&gt;The &lt;code&gt;toMatchInlineSnapshot()&lt;/code&gt; function starts out blank and is automatically filled in with that string template when the test is executed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Mistake #4: Writing tests in TypeScript
&lt;/h2&gt;

&lt;p&gt;TypeScript is great. Every single project that I create professionally or personally (my &lt;a href="https://www.posten.io"&gt;personal website&lt;/a&gt; included) is written in TypeScript. However, writing your &lt;strong&gt;tests&lt;/strong&gt; in TypeScript provides little to no value.&lt;/p&gt;

&lt;p&gt;In fact, more often than not your TypeScript test files end up having to define custom types of their own or do a bunch of funky typecasting in order to tell the TypeScript compiler to calm down and accept my fake data. Doing this makes the tests more difficult to maintain, harder to read, and simply creates cruft that does not add any value and has no reason to be there.&lt;/p&gt;

&lt;p&gt;This is a minor point, but TypeScript tests also usually require more work to setup because they have to be compiled, and you always have to tell typescript to add all the global functions that your tests reference. It's not that these things are difficult, they're just more setup to do that again...adds no value to your project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mistake #5: Having a describe() that wraps the entire test module
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;I admit that this final point is more just annoying than anything else.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you've ever worked with me you already know that I really hate repeating myself. I try quite hard to make my code as &lt;a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself"&gt;DRY&lt;/a&gt; as reasonably possible. So when I see duplication for the sake of duplication, I need to put a stop to it immediately.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// get-link.test.js&lt;/span&gt;

&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;get link handler&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should do this&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should do that&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What possible purpose could that &lt;code&gt;describe()&lt;/code&gt; serve? When the test is run, this is the output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PASS  get-link.test.ts
  get link handler
    ✓ should do this (4 ms)
    ✓ should do that (4 ms)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact same information is repeated on lines 1 and 2! For Pete's sake, just remove the pointless &lt;code&gt;describe()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The only defense of this tactic that I've heard is that it makes the code consistent if you later add a second &lt;code&gt;describe()&lt;/code&gt; in the file. But it would not make sense for &lt;code&gt;get-link.test.js&lt;/code&gt; to have any tests in it that didn't test "get link"; so the only way it could have another &lt;code&gt;describe()&lt;/code&gt; would be inside of the root one, in which case you can STILL remove the root one. 🙃&lt;/p&gt;

</description>
      <category>testing</category>
      <category>react</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>New git commands to replace 'checkout'</title>
      <dc:creator>Zach Olivare</dc:creator>
      <pubDate>Wed, 16 Feb 2022 20:36:04 +0000</pubDate>
      <link>https://dev.to/olivare/new-git-commands-to-replace-checkout-5boi</link>
      <guid>https://dev.to/olivare/new-git-commands-to-replace-checkout-5boi</guid>
      <description>&lt;p&gt;Git &lt;a href="https://github.com/git/git/blob/master/Documentation/RelNotes/2.23.0.txt"&gt;somewhat recently&lt;/a&gt; introduced two new commands, &lt;a href="https://git-scm.com/docs/git-switch"&gt;switch&lt;/a&gt; and &lt;a href="https://git-scm.com/docs/git-restore"&gt;restore&lt;/a&gt;. Together, these commands split up the functionality of the &lt;code&gt;git checkout&lt;/code&gt; command.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't worry, &lt;code&gt;checkout&lt;/code&gt; isn't going anywhere and you don't have to change your current workflows if you don't want to.&lt;/p&gt;

&lt;p&gt;People who are new to git may have an easier time understanding the purpose of these new commands than they would learning all the uses of checkout though, so it might still be worth your time to learn these new commands if you spend much time mentoring new devs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The old way
&lt;/h2&gt;

&lt;p&gt;At first glance it might seem odd that these new commands aren't introducing any new functionality. But consider all the different types of things that the &lt;code&gt;checkout&lt;/code&gt; command can do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Switch to an existing branch&lt;/span&gt;
git checkout &amp;lt;branch&amp;gt;

&lt;span class="c"&gt;# Create a new branch (and switch to it)&lt;/span&gt;
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; newBranch

&lt;span class="c"&gt;# Discard changes to a file&lt;/span&gt;
git checkout &lt;span class="nt"&gt;--&lt;/span&gt; &amp;lt;file&amp;gt;

&lt;span class="c"&gt;# Replace the contents of a file (with whatever's in main)&lt;/span&gt;
git checkout main &lt;span class="nt"&gt;--&lt;/span&gt; &amp;lt;file&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two categories of functionality happening in those examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first two commands switch which branch you're on (&lt;code&gt;git switch&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The second two commands replace the contents of files in your working directory (&lt;code&gt;git restore&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The new way
&lt;/h2&gt;

&lt;p&gt;Here's how you would perform those same tasks using switch and restore:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Switch to an existing branch&lt;/span&gt;
git switch &amp;lt;branch&amp;gt;

&lt;span class="c"&gt;# Create a new branch (and switch to it)&lt;/span&gt;
&lt;span class="c"&gt;# -c is short for --create&lt;/span&gt;
git switch &lt;span class="nt"&gt;--create&lt;/span&gt; newBranch

&lt;span class="c"&gt;# Discard changes to a file&lt;/span&gt;
git restore &amp;lt;file&amp;gt;

&lt;span class="c"&gt;# Replace the contents of a file (with whatever's in main)&lt;/span&gt;
git restore &amp;lt;file&amp;gt; &lt;span class="nt"&gt;--source&lt;/span&gt; main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;The &lt;code&gt;restore&lt;/code&gt; command also provides a (potentially more logical) new way to unstage files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# The old way&lt;/span&gt;
git reset HEAD &amp;lt;file&amp;gt;

&lt;span class="c"&gt;# The new way&lt;/span&gt;
git restore &lt;span class="nt"&gt;--staged&lt;/span&gt; &amp;lt;file&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The old way used the &lt;code&gt;reset&lt;/code&gt; command to copy from HEAD (your last commit) to the index (staging area). Because the HEAD and index now match, the change you made to the file only exists in your working directory, meaning the file is "unstaged".&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;reset&lt;/code&gt; command defaults to the &lt;code&gt;--mixed&lt;/code&gt; mode, which affects your HEAD and index. There is also &lt;code&gt;reset --soft&lt;/code&gt; which only affects the HEAD (and leaves the index and working directory untouched), and &lt;code&gt;reset --hard&lt;/code&gt; which moves the HEAD, index, and working directory.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;reset --hard&lt;/code&gt; is potentially destructive because it changes the working directory without committing or stashing it.&lt;/p&gt;

&lt;p&gt;For more on the reset command, there is a &lt;a href="https://git-scm.com/book/en/v2/Git-Tools-Reset-Demystified"&gt;fantastic section&lt;/a&gt; of the git book on the topic.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With the new way, you're stating that you want to "restore staged files" and by default, if  &lt;code&gt;--staged&lt;/code&gt; is given, the contents are restored from HEAD. The effect is the same as the old way, copying from HEAD to the index, "unstaging" the file.&lt;/p&gt;

&lt;p&gt;I've always felt that the old way was a roundabout way to accomplish unstaging a file, made extra confusing by the implied &lt;code&gt;--mixed&lt;/code&gt;. The new way is still not the most clear thing in the world, but in my opinion it's in incremental improvement.&lt;/p&gt;

&lt;p&gt;To clear that up, you should create a git alias!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; alias.unstage &lt;span class="s2"&gt;"restore --staged"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then you can use it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git unstage &amp;lt;file&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>git</category>
      <category>cli</category>
      <category>switch</category>
      <category>restore</category>
    </item>
    <item>
      <title>How to manipulate a Gremlin Cosmos DB from inside a Node.js Azure Function</title>
      <dc:creator>Zach Olivare</dc:creator>
      <pubDate>Wed, 22 Jul 2020 15:34:09 +0000</pubDate>
      <link>https://dev.to/olivare/how-to-manipulate-a-gremlin-cosmos-db-from-inside-a-node-js-azure-function-hod</link>
      <guid>https://dev.to/olivare/how-to-manipulate-a-gremlin-cosmos-db-from-inside-a-node-js-azure-function-hod</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This post assumes that you've already got an Azure Functions project set up and running locally on your computer. If not, please see to my &lt;a href="https://dev.to/zposten/how-to-get-started-with-azure-functions-in-node-js-45ke"&gt;previous post&lt;/a&gt; about how to do exactly that.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
So you've got a Functions project, and you've created a Cosmos DB in the azure portal, now how do you connect them?&lt;/p&gt;
&lt;h2&gt;
  
  
  The way it's supposed to work (but doesn't)
&lt;/h2&gt;

&lt;p&gt;The way that the interaction of writing to a database from within an Azure Function is supposed to work is &lt;a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-cosmosdb-v2-output?tabs=javascript"&gt;via a binding&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You create an entry in your &lt;code&gt;function.json&lt;/code&gt; file for an output binding to Cosmos DB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"employeeDocument"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cosmosDB"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"databaseName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-database"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"collectionName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"myCollection"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"createIfNotExists"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"connectionStringSetting"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MyAccount_COSMOSDB"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"direction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"out"&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;And then you use the &lt;code&gt;context.bindings&lt;/code&gt; object to write to the property you specified in the &lt;code&gt;name&lt;/code&gt; field above:&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nb"&gt;document&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;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Zach&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;employeeId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;004&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;221b Baker St&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bindings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;employeeDocument&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But that DOES NOT WORK!&lt;/p&gt;

&lt;p&gt;All that happens when this code runs is a vertex with no data is created in the Cosmos DB.&lt;/p&gt;

&lt;p&gt;Why is that? Because &lt;a href="https://github.com/Azure/azure-functions-python-worker/issues/413"&gt;Microsoft does not currently support&lt;/a&gt; binding to a Cosmos DB using the Gremlin API.&lt;/p&gt;

&lt;p&gt;All &lt;a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-integrate-store-unstructured-data-cosmosdb?tabs=javascript"&gt;of&lt;/a&gt; &lt;a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-cosmosdb-v2-input?tabs=javascript"&gt;their&lt;/a&gt; &lt;a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-cosmosdb-v2-output?tabs=javascript"&gt;examples&lt;/a&gt; (those are three separate links) use the SQL API, without anywhere saying that these bindings are only supported in the SQL API, and not in the Gremlin API.&lt;/p&gt;

&lt;p&gt;I'm guessing that the SQL API came out before the Gremlin API, and was at the time this documentation was written the only available API. So the Microsoft MVPs that wrote these "documentation" articles weren't aware that they should state that what they were doing was specific to SQL.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It still confuses me how this binding that supposedly does not exist manages to write anything at all to the DB, even if that is only an empty vertex. Perhaps the SQL code is still running behind the scenes and by some coincidence still has some effect on the Gremlin graph?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The way it actually works
&lt;/h2&gt;

&lt;p&gt;Now that I've hopefully helped you avoid the pitfall that stole hours of my life, what we're actually going to do is make use of the &lt;a href="https://www.npmjs.com/package/gremlin"&gt;gremlin NPM package&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Because of current limitations of Cosmos DB's Gremlin API, we'll see in this post that using the base gremlin NPM package is somewhat awkward and difficult. I've created a new library called &lt;a href="https://github.com/zposten/gremlin-cosmos"&gt;gremlin-cosmos&lt;/a&gt; to make the interface between Gremlin and Cosmos more developer friendly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Remember that because we're developing our functions locally, we can install NPM packages and then the &lt;code&gt;node_modules&lt;/code&gt; directory automatically gets pushed into Azure with your function code so that everything continues to work in the cloud.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i gremlin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installing the NPM package, first thing you need to do is define some configuration variables about your database. At the root of your project create a &lt;code&gt;SharedCode&lt;/code&gt; directory and inside of that create a &lt;code&gt;gremlinConfig.ts&lt;/code&gt; file.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: My examples are going to assume you're using typescript. If you're not, you'll have to change the file names to &lt;code&gt;.js&lt;/code&gt;, remove any typing information, and change the &lt;code&gt;import&lt;/code&gt; / &lt;code&gt;export&lt;/code&gt; syntax to use &lt;code&gt;require&lt;/code&gt; / &lt;code&gt;module.exports&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// SharedCode/gremlinConfig.ts&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wss://my-cosmos-db.gremlin.cosmos.azure.com:443&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;primaryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;superSuperSecretKey==&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-database&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myCollection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find each of these data in your Cosmos DB on the Azure portal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;endpoint&lt;/code&gt; can be found on the &lt;strong&gt;Overview&lt;/strong&gt; tab&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;primaryKey&lt;/code&gt; can be found on the &lt;strong&gt;Keys&lt;/strong&gt; tab&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;database&lt;/code&gt; can be found on the &lt;strong&gt;Data Explorer&lt;/strong&gt; tab, it is the top level item in the DATA section&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;collection&lt;/code&gt; can be found by expanding the (just described) database to show its collections&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you have the configuration down, you can then use that information to create a Gremlin client that you'll use to communicate with the database:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;config&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;../SharedCode/gremlinConfig&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&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;authenticator&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;Gremlin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PlainTextSaslAuthenticator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`/dbs/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/colls/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;primaryKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Gremlin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;authenticator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;traversalsource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;g&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;rejectUnauthorized&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;mimeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/vnd.gremlin-v2.0+json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the &lt;code&gt;Client&lt;/code&gt; has been crated, you can finally create your first vertex!&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`g.addV(label).property('pk', partitionKey).property('name', name)`&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;person&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Zach&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;partitionKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If it is not passed explicitly, the &lt;code&gt;id&lt;/code&gt; field will be auto assigned to a GUID that does not exist as a key in the database&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As you can see, the syntax here is quite unfortunate. You have to craft this query as a string. Values with quotes around them get treated as strings, and values without quotes get interpreted as keys in the object that you pass as the second argument to &lt;code&gt;client.submit()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Gremlin is &lt;a href="http://tinkerpop.apache.org/docs/current/reference/#_submitting_scripts_4"&gt;not supposed to be written like that&lt;/a&gt;, but has to be when working with Cosmos DB's Gremlin API because &lt;a href="https://docs.microsoft.com/en-us/azure/cosmos-db/gremlin-compatibility#unsupported-feature"&gt;Cosmos does not support&lt;/a&gt; Gremlin's nicer bytecode syntax.&lt;/p&gt;

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

&lt;p&gt;In this post, we've seen that Microsoft doesn't exactly make it easy to work with Cosmos DB's Gremlin API inside of an Azure Function. It doesn't have any bindings, and doesn't support the backend features necessary to make it easy to work with the gremlin NPM package either.&lt;/p&gt;

&lt;p&gt;As I hinted at earlier, I've created a wrapper around the gremlin NPM package to make it easier to work with Cosmos' Gremlin API; it's called &lt;a href="https://github.com/zposten/gremlin-cosmos"&gt;gremlin-cosmos&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>node</category>
      <category>gremlin</category>
      <category>cosmosdb</category>
    </item>
    <item>
      <title>How to get started with Azure Functions in Node.js</title>
      <dc:creator>Zach Olivare</dc:creator>
      <pubDate>Tue, 14 Jul 2020 17:33:25 +0000</pubDate>
      <link>https://dev.to/olivare/how-to-get-started-with-azure-functions-in-node-js-45ke</link>
      <guid>https://dev.to/olivare/how-to-get-started-with-azure-functions-in-node-js-45ke</guid>
      <description>&lt;p&gt;I was asked to write several Azure Functions in JavaScript that modified a Cosmos DB via its Gremlin API. When I was asked to do that, I had only a vague idea of what an Azure Function was, I had never touched Cosmos DB before and I couldn't begin to imagine what the hell a Gremlin API was.&lt;/p&gt;

&lt;p&gt;But after hours spent sorting through the confusing, incomplete assortment of Microsoft documentation on the topic, I did manage to produce some nice functions and gain an understanding of the various aspects of Azure Functions.&lt;/p&gt;

&lt;p&gt;In this post, I hope to help your getting started experience move along faster than mine did.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: There are &lt;a href="https://docs.microsoft.com/en-us/azure/azure-functions/supported-languages"&gt;a number of different languages&lt;/a&gt; that you can write Azure Functions in, in this article I will only be referencing the JavaScript (and TypeScript) languages specifically.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Why would you want to use an Azure Function?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://jamstack.org"&gt;Jamstack&lt;/a&gt; is growing in popularity, and for good reason! Jamstack sites are &lt;a href="https://serverless.css-tricks.com"&gt;serverless&lt;/a&gt;. Instead of storing source files on a dedicated server, the source files are instead served over a CDN for better (potentially global) performance.&lt;/p&gt;

&lt;p&gt;But if there's no server, then how do you interact with a database or any other backend service? That's where Azure Functions (or something like it) comes in.&lt;/p&gt;

&lt;p&gt;The idea behind Azure Functions is to have a blob of code that you can push to the cloud, without having to worry about ecosystem in which it runs. You don't have to create (for example) an &lt;a href="https://expressjs.com"&gt;express&lt;/a&gt; server and then figure out how to publish and run that server in the cloud. Instead, you just give Azure a blob of code and set some configuration options about when that code is run.&lt;/p&gt;

&lt;h3&gt;
  
  
  A quick overview of how they work
&lt;/h3&gt;

&lt;p&gt;There are 4 pieces that make up an Azure Function, as illustrated well by the Azure Portal:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Trigger&lt;/li&gt;
&lt;li&gt;Inputs&lt;/li&gt;
&lt;li&gt;The actual function code&lt;/li&gt;
&lt;li&gt;Outputs&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Pgw8390f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zr5xcfy592mg5155d426.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Pgw8390f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zr5xcfy592mg5155d426.png" alt="Parts of an azure function" width="880" height="181"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Trigger
&lt;/h4&gt;

&lt;p&gt;The function trigger is the condition that tells the function to run. &lt;a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-triggers-bindings#supported-bindings"&gt;Azure supports a wide variety of triggers&lt;/a&gt;; additions or mutations of Blob Storage, a change to a Cosmos DB, and an HTTP request to name a few.&lt;/p&gt;

&lt;p&gt;The trigger is passed as the second argument to your function, the first being the context object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myTrigger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myOtherInput&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;For HTTP triggers for example, the &lt;a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference-node#request-object"&gt;HTTP request object&lt;/a&gt; will be passed.&lt;/p&gt;

&lt;h4&gt;
  
  
  Inputs
&lt;/h4&gt;

&lt;p&gt;Inputs are passed as arguments to your function. The first input will be the third argument, the first being the context object and the second being the trigger.&lt;/p&gt;

&lt;p&gt;There are &lt;a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference-node#inputs"&gt;other ways&lt;/a&gt; to access the inputs too.&lt;/p&gt;

&lt;h4&gt;
  
  
  The actual function code
&lt;/h4&gt;

&lt;p&gt;One confusing thing about the function is the first argument that's always passed to it, the &lt;code&gt;context&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;The context is basically just an object jam-packed with everything that Microsoft wants to give your function access to.&lt;/p&gt;

&lt;p&gt;For example you can call &lt;code&gt;context.log()&lt;/code&gt; to write trace output to the console. You can also call &lt;code&gt;context.log.warn()&lt;/code&gt;, &lt;code&gt;context.log.error()&lt;/code&gt; and &lt;code&gt;context.log.verbose()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The context is also where you'll access the bindings you've configured via &lt;code&gt;context.bindings&lt;/code&gt;. HTTP request triggers and HTTP response outputs are so common, that in addition to being able to access theme via &lt;code&gt;context.bindings.req&lt;/code&gt; and &lt;code&gt;context.bindings.res&lt;/code&gt; respectively, they also exist directly on the context object as &lt;code&gt;context.req&lt;/code&gt; and &lt;code&gt;context.res&lt;/code&gt;. That's in addition to the &lt;code&gt;req&lt;/code&gt; being injected as the second argument to your function. They give you plenty of rope to hang yourself with here, but just pick your favorite and be consistent.&lt;/p&gt;

&lt;h4&gt;
  
  
  Outputs
&lt;/h4&gt;

&lt;p&gt;Outputs are the side effects that your function produces, like creating or updating an entry in your database.&lt;/p&gt;

&lt;p&gt;When the Microsoft documentation &lt;a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference-node#outputs"&gt;talks about outputs&lt;/a&gt;, they're exclusively referencing their &lt;a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-triggers-bindings#supported-bindings"&gt;available&lt;/a&gt; output &lt;strong&gt;bindings&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A binding is basically a way of interacting with some other system that Microsoft has baked into the Azure function for you. But because you have access to NPM packages (I talk about how to install them below), you're not limited to just those bindings.&lt;/p&gt;

&lt;p&gt;For example, Microsoft does &lt;a href="https://github.com/Azure/azure-functions-python-worker/issues/413"&gt;not currently support&lt;/a&gt; binding to a Cosmos DB using the Gremlin API. To do this, I had to make use of the &lt;a href="https://www.npmjs.com/package/gremlin"&gt;gremlin&lt;/a&gt; NPM package.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where to start
&lt;/h3&gt;

&lt;p&gt;Before I talk about how to get started, I wanted to clarify something that confused me initially.&lt;/p&gt;

&lt;p&gt;Your goal is to set up a Functions project (the Azure portal calls it a Function App), not a singular function. So if you use the portal to create a Function, you're creating one Function inside of a Functions project. If you use an IDE, you're creating the structure of the project first and then individual functions inside of that project.&lt;/p&gt;

&lt;h4&gt;
  
  
  Probably don't use the portal
&lt;/h4&gt;

&lt;p&gt;It's possible to develop Azure Functions without ever leaving the Azure portal. But it sucks. Their online code editor is frustrating for even the simplest of changes, and while using the portal it's not possible to also use any NPM library.&lt;/p&gt;

&lt;p&gt;Additionally, and perhaps most importantly, writing your Functions in the portal prevents you from storing them in version control! One accidental click and all the work you've done can be deleted.&lt;/p&gt;

&lt;p&gt;Using the portal can be a nice way to just write &lt;em&gt;something&lt;/em&gt; up there and see it run quick, but even for that use case I think you would be better off...&lt;/p&gt;

&lt;h4&gt;
  
  
  Using VS Code
&lt;/h4&gt;

&lt;p&gt;VS Code has an &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions"&gt;Azure Functions extension&lt;/a&gt; that is incredibly easy to use and makes the whole process of setting up a Functions project much easier.&lt;/p&gt;

&lt;p&gt;I would suggest just following the steps laid out on the extension's home page. They walk you through creating a project and creating your first Function. Keep in mind that if you've messed around on the portal, you can use the Function project that you've already created to save on paying for additional resources.&lt;/p&gt;

&lt;p&gt;One other area you might deviate from their walkthough is in how you run your Function project locally. They suggest using the integrated VS Code debugger. You can definitely do that if you want to, but I prefer running scripts in my own terminal outside of VS Code.&lt;/p&gt;

&lt;p&gt;To do so, from the root of the project run &lt;code&gt;npm start&lt;/code&gt; in your terminal. This does the same thing that the VS Code integrated debugger would do; using the pre-installed &lt;code&gt;@azure/functions&lt;/code&gt; NPM package to watch your code and restart the server when you make changes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When I used the VS Code native debugger, it would crash every time I made changes to the source files, forcing me to re-run it each time, which was really annoying. That bug could very well be fixed by the time you're reading this though.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Installing NPM packages
&lt;/h4&gt;

&lt;p&gt;You can bolster the power of Azure Functions by installing and using libraries from NPM.&lt;/p&gt;

&lt;p&gt;Doing so is as simple as running &lt;code&gt;npm install&lt;/code&gt; in the root of the project (as you would expect) and then &lt;code&gt;require&lt;/code&gt;ing or &lt;code&gt;import&lt;/code&gt;ing them exactly as you normally would.&lt;/p&gt;

&lt;p&gt;When you deploy your functions to azure, your &lt;code&gt;node_modules&lt;/code&gt; directory will be deployed as well so that everything continues to run the same once deployed as it ran locally.&lt;/p&gt;

&lt;h4&gt;
  
  
  JavaScript vs TypeScript
&lt;/h4&gt;

&lt;p&gt;When you use the VS Code extension to initialize your Functions project, you get to choose between JavaScript and TypeScript for your project.&lt;/p&gt;

&lt;p&gt;This choice is largely preference based, but I feel that TypeScript has two big advantages here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Strong typing is extremely useful when you're working with a database.

&lt;ul&gt;
&lt;li&gt;It's just too easy to mess up your data-structure when writing a different query and then have to annoyingly go back and fix it.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Using TypeScript also allows you to use the ES6 module syntax for imports and exports instead of NPM's &lt;code&gt;module.exports&lt;/code&gt; syntax, which I strongly prefer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The only disadvantage that I've found is that sourcemaps don't see seem to work, so your errors don't point to your original source code, but it's generally pretty easy to figure that out for yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Resources
&lt;/h3&gt;

&lt;p&gt;On the whole, I found Microsoft's documentation around Azure Functions rather confusing and disappointing. Perhaps they do have some great docs somewhere, but it's really hard to find the one you're looking for when you're not sure what that is.&lt;/p&gt;

&lt;p&gt;The piece of documentation that helped me the most though (and the one that I linked to several times in this post) was definitely their &lt;a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference-node#dependency-management"&gt;Azure Functions JavaScript developer guide&lt;/a&gt;. It explains the nitty-gritty details of a lot of what I talked about in this post, and is a good place to reference when you're trying to do something specific that's not working.&lt;/p&gt;

&lt;p&gt;If you're looking for more information on how to work with the Gremlin API of a Cosmos DB, I should have another post about that specifically coming soon!&lt;/p&gt;

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