<?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>Forem: Lenmor Ld</title>
    <description>The latest articles on Forem by Lenmor Ld (@lennythedev).</description>
    <link>https://forem.com/lennythedev</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%2F121411%2Fd5c5a3e7-0c97-4715-af6e-c194416f232b.jpeg</url>
      <title>Forem: Lenmor Ld</title>
      <link>https://forem.com/lennythedev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/lennythedev"/>
    <language>en</language>
    <item>
      <title>React Workshop - free online workshop by SCS Concordia</title>
      <dc:creator>Lenmor Ld</dc:creator>
      <pubDate>Tue, 27 Oct 2020 21:08:22 +0000</pubDate>
      <link>https://forem.com/lennythedev/react-workshop-free-online-workshop-by-scs-concordia-5dgc</link>
      <guid>https://forem.com/lennythedev/react-workshop-free-online-workshop-by-scs-concordia-5dgc</guid>
      <description>&lt;p&gt;Join us next week for a React workshop by SCS Concordia (Montreal) in collaboration with Autodesk!&lt;/p&gt;

&lt;p&gt;This workshop will cover concepts, examples, and exercises to help you build the UI for your next project. React is a frontend Javascript library that allows you to build interactive UIs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Registration: Workshop happening at November 5 (Thursday) 17:30 EST
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Sign up through Facebook events: &lt;br&gt;
&lt;a href="https://www.facebook.com/events/1818957074910385"&gt;https://www.facebook.com/events/1818957074910385&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;or sign up directly through this Google Form:&lt;br&gt;
&lt;a href="https://forms.gle/pU4gcKTLugaXaTmb8"&gt;https://forms.gle/pU4gcKTLugaXaTmb8&lt;/a&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Topics
&lt;/h3&gt;

&lt;p&gt;Talk topics include but are not limited to:&lt;/p&gt;

&lt;p&gt;✅ What &lt;strong&gt;React&lt;/strong&gt; is and what you can do with it&lt;br&gt;
✅ Modern Javascript (&lt;strong&gt;ES6&lt;/strong&gt;) syntax and tricks&lt;br&gt;
✅ Writing &lt;strong&gt;JSX&lt;/strong&gt; and rendering elements to HTML&lt;br&gt;
✅ Building &lt;strong&gt;components&lt;/strong&gt;, composition and splitting&lt;br&gt;
✅ Displaying &lt;strong&gt;lists&lt;/strong&gt; of item objects using components&lt;br&gt;
✅ Understanding React data flow with &lt;strong&gt;props&lt;/strong&gt; and &lt;strong&gt;state&lt;/strong&gt;&lt;br&gt;
✅ Using &lt;strong&gt;Hooks&lt;/strong&gt; in function components&lt;br&gt;
✅ Adding interactivity with HTML &lt;strong&gt;inputs&lt;/strong&gt;, &lt;strong&gt;forms&lt;/strong&gt; and &lt;strong&gt;event-handling&lt;/strong&gt;&lt;br&gt;
✅ &lt;strong&gt;Fetching&lt;/strong&gt; data from public &lt;strong&gt;API&lt;/strong&gt;s&lt;br&gt;
✅ Building a &lt;strong&gt;project&lt;/strong&gt; while thinking in React&lt;/p&gt;

&lt;h3&gt;
  
  
  Preview
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ln3_feIg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1603799558/react_workshop_slides/2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ln3_feIg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1603799558/react_workshop_slides/2.png" alt="react workshop preview 1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--W2NJW6ib--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1603799558/react_workshop_slides/2b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--W2NJW6ib--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1603799558/react_workshop_slides/2b.png" alt="react workshop preview 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tvlSNbCX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1603799558/react_workshop_slides/3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tvlSNbCX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1603799558/react_workshop_slides/3.png" alt="react workshop preview 3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qzjPQTZE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1603799558/react_workshop_slides/4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qzjPQTZE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1603799558/react_workshop_slides/4.png" alt="react workshop preview 4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8Ltjmc_l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1603799558/react_workshop_slides/5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8Ltjmc_l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1603799558/react_workshop_slides/5.png" alt="react workshop preview 5"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aZ7JlXsn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1603799558/react_workshop_slides/6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aZ7JlXsn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1603799558/react_workshop_slides/6.png" alt="react workshop preview 6"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BnGVc2DM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1603799558/react_workshop_slides/7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BnGVc2DM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1603799558/react_workshop_slides/7.png" alt="react workshop preview 7"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--e8Yh85SA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1603799558/react_workshop_slides/8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--e8Yh85SA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1603799558/react_workshop_slides/8.png" alt="react workshop preview 8"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is an online event that will be conducted live on &lt;strong&gt;YouTube&lt;/strong&gt;. The link will be provided prior to the event, so make sure you &lt;strong&gt;subscribe&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/channel/UCvLvkP20J4GGKLEG0nnzvzg"&gt;https://www.youtube.com/channel/UCvLvkP20J4GGKLEG0nnzvzg&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See you there! 🤓 &lt;br&gt;
👩‍💻 👨‍💻&lt;/p&gt;

</description>
      <category>react</category>
      <category>beginners</category>
      <category>workshop</category>
      <category>ui</category>
    </item>
    <item>
      <title>Testing async stuff in React components with Jest and react-testing-library</title>
      <dc:creator>Lenmor Ld</dc:creator>
      <pubDate>Sat, 17 Oct 2020 20:41:05 +0000</pubDate>
      <link>https://forem.com/lennythedev/testing-async-stuff-in-react-components-with-jest-and-react-testing-library-mag</link>
      <guid>https://forem.com/lennythedev/testing-async-stuff-in-react-components-with-jest-and-react-testing-library-mag</guid>
      <description>&lt;p&gt;&lt;em&gt;Initially posted in &lt;a href="https://lennythedev.com/blog/testing_async_in_react_components_with_react_testing_library" rel="noopener noreferrer"&gt;lennythedev.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When testing React components with async state changes, like when data fetching with &lt;code&gt;useEffect&lt;/code&gt;, you might get this error:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1602539351%2Flennythedev%2Fnot_wrapped_in_act_error.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1602539351%2Flennythedev%2Fnot_wrapped_in_act_error.png" alt="not wrapped in act error"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Issue&lt;/strong&gt;&lt;/p&gt;

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

Warning: An update to &amp;lt;SomeComponent&amp;gt; inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...)


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;When using plain &lt;code&gt;react-dom/test-utils&lt;/code&gt; or &lt;code&gt;react-test-renderer&lt;/code&gt;,  wrap each and every state change in your component with an &lt;code&gt;act()&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When using React Testing Library, use &lt;strong&gt;async utils&lt;/strong&gt; like &lt;code&gt;waitFor&lt;/code&gt; and &lt;code&gt;findBy...&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Async example - data fetching effect in &lt;code&gt;useEffect&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;You have a React component that fetches data with &lt;code&gt;useEffect&lt;/code&gt;.&lt;br&gt;
Unless you're using the &lt;a href="https://reactjs.org/docs/concurrent-mode-suspense.html" rel="noopener noreferrer"&gt;experimental Suspense&lt;/a&gt;, you have something like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Loading/placeholder view&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When data is not there yet, you may display a placeholder UI like a spinner, "Loading..." or some skeleton item. &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Data view&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When data arrives, you set data to your state so it gets displayed in a Table, mapped into &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt;s, or any data visualization have you.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1602540049%2Flennythedev%2Freact_testing_library_fetchy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1602540049%2Flennythedev%2Freact_testing_library_fetchy.gif" alt="fetch component example"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&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;Fetchy&lt;/span&gt; &lt;span class="o"&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// simulate a fetch&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Fetchy&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h3&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;d&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;d&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="p"&gt;))}&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Fetchy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;
&lt;h1&gt;
  
  
  Testing a data fetch
&lt;/h1&gt;

&lt;p&gt;😎 Now, you want to test this.&lt;br&gt;
Here, we're using &lt;em&gt;React Testing Library&lt;/em&gt;, but the concepts apply to &lt;em&gt;Enzyme&lt;/em&gt; as well.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Here's a good intro to &lt;a href="https://testing-library.com/docs/react-testing-library/intro" rel="noopener noreferrer"&gt;React Testing Library&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;only&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fetchy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;beforeAll&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;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useFakeTimers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nf"&gt;afterAll&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;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useRealTimers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&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="s2"&gt;shows Loading&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="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Fetchy&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;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Loading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;advanceTimersByTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Data:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&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;&lt;code&gt;getByText()&lt;/code&gt; finds element on the page that contains the given text. For more info on queries: &lt;a href="https://testing-library.com/docs/dom-testing-library/api-queries#queries" rel="noopener noreferrer"&gt;RTL queries&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Render component&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;screen.debug()&lt;/code&gt; logs the current HTML of document.body&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Assert Loading UI. It logs:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

...
&amp;lt;div&amp;gt;Loading&amp;lt;/div&amp;gt;
...
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;4. Simulate to the time data arrives, by fast-forwarding 3 seconds. `jest.advanceTimersByTime` lets us do this
5. `screen.debug()`
6. Assert Data UI. It logs:
    ```


    ...
    &amp;lt;h3&amp;gt;Data:&amp;lt;/h3&amp;gt;
    &amp;lt;div&amp;gt;1&amp;lt;/div&amp;gt;
    &amp;lt;div&amp;gt;2&amp;lt;/div&amp;gt;
    &amp;lt;div&amp;gt;3&amp;lt;/div&amp;gt;
    ...


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

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🕐 Note that we use &lt;code&gt;jest.advanceTimersByTime&lt;/code&gt; to fake clock ticks. This is so test runner / CI don't have to actually waste time waiting.&lt;br&gt;
To make it work, put &lt;code&gt;jest.useFakeTimers&lt;/code&gt; on setup and &lt;code&gt;jest.useRealTimers&lt;/code&gt; on teardown&lt;/p&gt;

&lt;p&gt;🖥 You can also put a selector here like &lt;code&gt;screen.debug(screen.getByText('test'))&lt;/code&gt;. For more info: &lt;a href="https://testing-library.com/docs/dom-testing-library/api-queries#screendebug" rel="noopener noreferrer"&gt;RTL screen.debug&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;✅ Tests pass...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1602539351%2Flennythedev%2Fasync_tests_pass.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1602539351%2Flennythedev%2Fasync_tests_pass.png" alt="all tests pass"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;😱 but we're getting some console warnings 🔴&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1602539351%2Flennythedev%2Fconsole_warnings_act_error.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1602539351%2Flennythedev%2Fconsole_warnings_act_error.png" alt="console warnings act error"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that it's not the &lt;code&gt;screen.debug&lt;/code&gt; since even after commenting it out, the same warning shows.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Wait, what is &lt;code&gt;act()&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;Part of React DOM test utils, &lt;code&gt;act()&lt;/code&gt; is used to wrap renders and updates inside it, to prepare the component for assertions. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://reactjs.org/docs/test-utils.html#act" rel="noopener noreferrer"&gt;📚 Read more: act() in React docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The error we got reminds us that all state updates must be accounted for, so that the test can "act" like it's running in the browser. &lt;/p&gt;

&lt;p&gt;In our case, when the data arrives after 3 seconds, the &lt;code&gt;data&lt;/code&gt; state is updated, causing a re-render. The test has to know about these state updates, to allow us to assert the UI changes before and after the change.&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;Warning&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;An&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;Fetchy&lt;/span&gt; &lt;span class="nx"&gt;inside&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="nx"&gt;was&lt;/span&gt; &lt;span class="nx"&gt;not&lt;/span&gt; &lt;span class="nx"&gt;wrapped&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;act&lt;/span&gt;&lt;span class="p"&gt;(...).&lt;/span&gt;
&lt;span class="nx"&gt;When&lt;/span&gt; &lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="nx"&gt;that&lt;/span&gt; &lt;span class="nx"&gt;causes&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="nx"&gt;updates&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;be&lt;/span&gt; &lt;span class="nx"&gt;wrapped&lt;/span&gt; &lt;span class="nx"&gt;into&lt;/span&gt; &lt;span class="nf"&gt;act&lt;/span&gt;&lt;span class="p"&gt;(...):&lt;/span&gt;
&lt;span class="nf"&gt;act&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="cm"&gt;/* fire events that update state */&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="cm"&gt;/* assert on the output */&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Coming back to the error message, it seems that we just have to wrap the render in &lt;code&gt;act()&lt;/code&gt;. &lt;br&gt;
The error message even gives us a nice snippet to follow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping state updates in &lt;code&gt;act()&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Wrap render in &lt;code&gt;act()&lt;/code&gt;
&lt;/h3&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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="s2"&gt;shows Loading&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="nf"&gt;act&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Fetchy&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="p"&gt;});&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;😭 Oh no, we're still getting the same error...&lt;/p&gt;

&lt;p&gt;Wrapping the render inside &lt;code&gt;act&lt;/code&gt; allowed us to catch the state updates on the first render, &lt;strong&gt;but we never caught the next update&lt;/strong&gt; which is when data arrives after 3 seconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrap in &lt;code&gt;act()&lt;/code&gt; with mock timer
&lt;/h3&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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="s2"&gt;shows Loading and Data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;act&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Fetchy&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="nf"&gt;act&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;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;advanceTimersByTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&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;🎉 Awesome! It passes and no more errors!&lt;/p&gt;

&lt;h3&gt;
  
  
  Using async utils in React Testing Library
&lt;/h3&gt;

&lt;p&gt;React Testing Library provides &lt;strong&gt;async utilities&lt;/strong&gt; to for more declarative and idiomatic testing.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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="s2"&gt;shows Loading and Data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Fetchy&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Loading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Data:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&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;ol&gt;
&lt;li&gt;
&lt;p&gt;Instead of wrapping the render in &lt;code&gt;act()&lt;/code&gt;, we just let it render normally. Then, we catch the async state updates by &lt;code&gt;await&lt;/code&gt;-ing the assertion.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;findBy*&lt;/code&gt; queries are special, that they return a promise that resolves when the element is eventually found&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We don't even need the &lt;code&gt;advanceTimersByTime&lt;/code&gt; anymore, since we can also just await the data to be loaded.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;screen.debug()&lt;/code&gt; only after the &lt;code&gt;await&lt;/code&gt;, to get the updated UI&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This way, we are testing the component closer to how the user uses and sees it in the browser in the real world. No fake timers nor catching updates manually.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📚 Read more: &lt;a href="https://testing-library.com/docs/vue-testing-library/cheatsheet#async-utilities" rel="noopener noreferrer"&gt;RTL async utilities&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;❌😭 Oh no! Tests are failing again!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1602539351%2Flennythedev%2Ffind_by_timeout_error.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1602539351%2Flennythedev%2Ffind_by_timeout_error.png" alt="findBy timeout error"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that if you have the jest fake timers enabled for the test where you're using async utils like &lt;code&gt;findBy*&lt;/code&gt;, it will take longer to timeout, since it's a fake timer after all 🙃&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Timeouts
&lt;/h3&gt;

&lt;p&gt;The default timeout of &lt;code&gt;findBy*&lt;/code&gt; queries is 1000ms (1 sec), which means it will fail if it doesn't find the element after 1 second. &lt;/p&gt;

&lt;p&gt;Sometimes you want it to wait longer before failing, like for our 3 second fetch. &lt;br&gt;
We can add a &lt;code&gt;timeout&lt;/code&gt; in the third parameter object &lt;code&gt;waitForOptions&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1602539351%2Flennythedev%2Ffind_by_timeout_options.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1602539351%2Flennythedev%2Ffind_by_timeout_options.png" alt="findBy timeout options"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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="s2"&gt;shows Loading and Data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Fetchy&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Loading&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="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt; &lt;span class="p"&gt;})).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Data:&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="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;})).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1602539351%2Flennythedev%2Ffind_by_timeout_passes.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1602539351%2Flennythedev%2Ffind_by_timeout_passes.png" alt="findBy timeout test passes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;✅😄 All green finally!&lt;/p&gt;

&lt;h3&gt;
  
  
  Other async utils
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;findBy*&lt;/code&gt; is a combination of &lt;code&gt;getBy*&lt;/code&gt; and &lt;code&gt;waitFor&lt;/code&gt;. You can also do:&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;await&lt;/span&gt; &lt;span class="nf"&gt;waitFor&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;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Loading&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&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;📚 More details on findBy: &lt;a href="https://testing-library.com/docs/dom-testing-library/api-queries#findby" rel="noopener noreferrer"&gt;RTL findBy&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Async example 2 - an async state change
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1602540049%2Flennythedev%2Freact_testing_library_checky.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1602540049%2Flennythedev%2Freact_testing_library_checky.gif" alt="async checkbox state example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Say you have a simple checkbox that does some async calculations when clicked.&lt;br&gt;
We'll simulate it here with a 2 second delay before the &lt;code&gt;label&lt;/code&gt; is updated:&lt;/p&gt;

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

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&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;Checky&lt;/span&gt; &lt;span class="o"&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isChecked&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setChecked&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&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;handleCheck&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// simulate a delay in state change&lt;/span&gt;
        &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;setChecked&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;prevChecked&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;prevChecked&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;2000&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Checky&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h4&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="na"&gt;change&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="nx"&gt;second&lt;/span&gt; &lt;span class="nx"&gt;delay&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h4&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkbox&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleCheck&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checky2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="nx"&gt;htmlFor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checky2&lt;/span&gt;&lt;span class="dl"&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;isChecked&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/label&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Checky&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Wrap in &lt;code&gt;act()&lt;/code&gt; with mock timer
&lt;/h3&gt;

&lt;p&gt;Testing with &lt;code&gt;act()&lt;/code&gt; can look like this:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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="s2"&gt;updates state with delay - act() + mock timers&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="nf"&gt;act&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Checky&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="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&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;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;false&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nf"&gt;act&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;fireEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;advanceTimersByTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&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;ol&gt;
&lt;li&gt;Render component, wrap in &lt;code&gt;act()&lt;/code&gt; to catch the initial state&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;screen.debug()&lt;/code&gt; to see HTML of initial UI&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

...
&amp;lt;input id="checky2" type="checkbox" /&amp;gt;
&amp;lt;label for="checky2"&amp;gt;false&amp;lt;/label&amp;gt;
...
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3. Assert initial UI: "false" label
4. Click the label using `fireEvent`
5. Simulate to the time state is updated arrives, by fast-forwarding 2 seconds. `jest.advanceTimersByTime`
5. `screen.debug()`
6. Assert updated UI with label "true"
    ```


    ...
    &amp;lt;input id="checky2" type="checkbox" /&amp;gt;
    &amp;lt;label for="checky2"&amp;gt;true&amp;lt;/label&amp;gt;
    ...


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Using async utils in React Testing Library
&lt;/h3&gt;

&lt;p&gt;Like in the first example, we can also use &lt;strong&gt;async utils&lt;/strong&gt; to simplify the test.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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="s2"&gt;updates state with delay - RTL async utils&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="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Checky&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;label&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;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;false&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&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="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt; &lt;span class="p"&gt;})).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// await waitFor(() =&amp;gt; screen.getByLabelText("true"), { timeout: 2000 });&lt;/span&gt;
    &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;As before, &lt;code&gt;await&lt;/code&gt; when the label we expect is found. Remember that we have to use &lt;code&gt;findBy*&lt;/code&gt; which returns a promise that we can await. &lt;/p&gt;

&lt;p&gt;Timeout is needed here since we are not under jest's fake timers, and state change only happens after 2 seconds.&lt;/p&gt;

&lt;p&gt;An alternative to &lt;code&gt;expect(await screen.findBy...)&lt;/code&gt; is &lt;code&gt;await waitFor(() =&amp;gt; screen.getBy...);&lt;/code&gt;.&lt;br&gt;
getBy* commands fail if not found, so &lt;code&gt;waitFor&lt;/code&gt; waits until getBy* succeeds.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1602539351%2Flennythedev%2Fchecky_all_good.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1602539351%2Flennythedev%2Fchecky_all_good.png" alt="Checky all good"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;✅ All good! Tests passes and no warnings! 😄💯&lt;/p&gt;

&lt;h1&gt;
  
  
  Code
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/lenmorld/react-test-library-boilerplate" rel="noopener noreferrer"&gt;https://github.com/lenmorld/react-test-library-boilerplate&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Further reading
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;For a more in-depth discussion on fixing the &lt;code&gt;"not wrapped in act(...)" warning&lt;/code&gt; and more examples in both Class and Function components, see this article by Kent C Dodds&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning" rel="noopener noreferrer"&gt;https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Common mistakes when using React Testing Library&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kentcdodds.com/blog/common-mistakes-with-react-testing-library" rel="noopener noreferrer"&gt;https://kentcdodds.com/blog/common-mistakes-with-react-testing-library&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Here's the Github issue that I found when I struggled with this error before&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/testing-library/react-testing-library/issues/667" rel="noopener noreferrer"&gt;https://github.com/testing-library/react-testing-library/issues/667&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

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

&lt;p&gt;🙌 That's all for now! Hope this helps when you encounter that dreaded &lt;code&gt;not wrapped in act(...)&lt;/code&gt; error and gives you more confidence when testing async behavior in your React components with React Testing Library. 👍&lt;/p&gt;

</description>
      <category>react</category>
      <category>test</category>
      <category>async</category>
      <category>jest</category>
    </item>
    <item>
      <title>Node Workshop part 2 - free online workshop by SCS Concordia</title>
      <dc:creator>Lenmor Ld</dc:creator>
      <pubDate>Sat, 22 Aug 2020 13:12:57 +0000</pubDate>
      <link>https://forem.com/lennythedev/node-workshop-part-2-free-online-workshop-by-scs-concordia-4e37</link>
      <guid>https://forem.com/lennythedev/node-workshop-part-2-free-online-workshop-by-scs-concordia-4e37</guid>
      <description>&lt;p&gt;Come learn about web technologies at second installment of the Node.js workshop! &lt;/p&gt;

&lt;p&gt;This workshop will cover code and concepts to help you build a solid foundation for your next web project! &lt;/p&gt;

&lt;h3&gt;
  
  
  Registration: Workshop happening at August 27 (Thursday) 17:30 EST
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Sign up through Facebook events: &lt;br&gt;
&lt;a href="https://www.facebook.com/events/751112552346326" rel="noopener noreferrer"&gt;https://www.facebook.com/events/751112552346326&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;or sign up directly through this Google Form:&lt;br&gt;
 &lt;a href="https://forms.gle/oa8TLc3J5Yh1F11E9" rel="noopener noreferrer"&gt;https://forms.gle/oa8TLc3J5Yh1F11E9&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Topics
&lt;/h3&gt;

&lt;p&gt;This is part 2 of the previous Node Workshop, the content will continue where we left off on part 1:&lt;/p&gt;

&lt;p&gt;Discussed in Part 1&lt;br&gt;
✅ use Node and Express to build a web server and REST API&lt;br&gt;
✅ understand routing, request and response&lt;br&gt;
✅ implement CRUD with HTTP methods&lt;/p&gt;

&lt;p&gt;Recording of Part 1: &lt;a href="https://www.youtube.com/watch?v=F7adheneS-8" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=F7adheneS-8&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Will be discussed in Part 2&lt;br&gt;
🔲 build a server-rendered website using templates&lt;br&gt;
🔲 connect to a Cloud NoSQL database: MongoDB Atlas DB&lt;br&gt;
🔲 user authentication with sessions, cookies and tokens&lt;br&gt;
🔲 using external APIs, such as Github Jobs, Giphy, Spotify and more!&lt;/p&gt;

&lt;h3&gt;
  
  
  Preview
&lt;/h3&gt;

&lt;p&gt;Here's a quick preview of the content, including a review of part 1 if you missed it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.google.com/presentation/d/10L2wbMPKmtNYBb7i1kO9JPAoHoPICLLcZGNMbrK0IRQ/edit?usp=sharing" rel="noopener noreferrer"&gt;Workshop preview in Google slides&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Screenshots
&lt;/h3&gt;

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

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

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

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

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

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

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

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

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

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

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

&lt;p&gt;*Note: This is an online event that will be conducted live on YouTube. The link will be provided prior to the event, so make sure you sign up!&lt;/p&gt;

&lt;p&gt;See you there! 🤓 &lt;br&gt;
👩‍💻 👨‍💻&lt;/p&gt;

</description>
      <category>node</category>
      <category>mongodb</category>
      <category>beginners</category>
      <category>workshop</category>
    </item>
    <item>
      <title>Show off Github repos in your Gatsby site using Github GraphQL API</title>
      <dc:creator>Lenmor Ld</dc:creator>
      <pubDate>Thu, 23 Jul 2020 00:32:21 +0000</pubDate>
      <link>https://forem.com/lennythedev/show-off-your-github-repos-in-your-gatsby-site-using-graphql-421l</link>
      <guid>https://forem.com/lennythedev/show-off-your-github-repos-in-your-gatsby-site-using-graphql-421l</guid>
      <description>&lt;p&gt;Want to show off your Github repositories in your Gatsby site? 👨‍💼👩‍💼&lt;/p&gt;

&lt;p&gt;Preview:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1595462572%2Fdevto%2Fgatsby_graphql_api%2Fgithub_repos_in_gatsby.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1595462572%2Fdevto%2Fgatsby_graphql_api%2Fgithub_repos_in_gatsby.gif" alt="Github repos in Gatsby"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even if you don't know GraphQL, this guide shows you just enough GraphQL to get you started learning it and using it. 🤓&lt;/p&gt;

&lt;p&gt;We'll use Github GraphQL API v4 to get all the repositories from your Github account and display it in your Gatsby site! 📊&lt;/p&gt;

&lt;p&gt;Let's get into it! 🏃‍♀️🏃‍♂️&lt;/p&gt;

&lt;h1&gt;
  
  
  GraphQL
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Intro to GraphQL
&lt;/h2&gt;

&lt;p&gt;A GraphQL API allows us to more efficiently create and consume APIs.&lt;/p&gt;

&lt;p&gt;For example, we might fetch something like this using REST:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GET &lt;code&gt;/api/:name/projects&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;GET &lt;code&gt;/api/:name/projects/:project_id&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;GET &lt;code&gt;/api/:name/projects/:project_id/description&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;GET &lt;code&gt;/api/:name/projects/:project_id/name&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;GET &lt;code&gt;/api/:name/projects/:project_id/watchers&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;GET &lt;code&gt;/api/:name/projects/:project_id/watchers/:watcher_id&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;GET &lt;code&gt;/api/:name/projects/:project_id/watchers/:watcher_id/name&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In GraphQL, we don't have to "overfetch" and just get all data we need all at once from one endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;query { 
  user(name: "myname") { 
    projects {
      name
      description
      watchers {
        name
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That's just the &lt;em&gt;tip of the iceberg&lt;/em&gt; for GraphQL. 🏔 ❄&lt;br&gt;
For a more detailed guide to GraphQL:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/davinc" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F195923%2Ff2aa9fc7-856c-4712-9298-428001fdbb41.jpeg" alt="davinc"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/davinc/graphql-for-beginners-3f1a" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;GraphQL for beginners&lt;/h2&gt;
      &lt;h3&gt;Danijel Vincijanović ・ Feb 14 '20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#graphql&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tutorial&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#beginners&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  Github GraphQL API
&lt;/h2&gt;

&lt;p&gt;Github provides a GraphQL API in their v4 update.&lt;/p&gt;

&lt;p&gt;They even provided a GraphiQL instance named "Gituhb GraphQL API explorer", which is basically an interactive "sandbox" for testing out queries on live Github data. 🧪&lt;/p&gt;

&lt;p&gt;This is similar to the GraphiQL you can access locally on your Gatsby site, normally on &lt;code&gt;http://localhost:8000/___graphql&lt;/code&gt;, but with the context of your Github account&lt;/p&gt;

&lt;h2&gt;
  
  
  Github GraphQL API explorer
&lt;/h2&gt;

&lt;p&gt;Go to &lt;a href="https://developer.github.com/v4/explorer/" rel="noopener noreferrer"&gt;Github GraphQL API explorer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After you sign-in to github, you can now make queries!&lt;br&gt;
Try this one...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
  &lt;/span&gt;&lt;span class="n"&gt;viewer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;repositories&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;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;...which gets your Github &lt;code&gt;login&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, and names of your first 10 repositories.&lt;br&gt;
The &lt;code&gt;node&lt;/code&gt; here represent each of the repositories found, which we can get the fields &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; from.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1595462534%2Fdevto%2Fgatsby_graphql_api%2Fgithub_graphql_explorer.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1595462534%2Fdevto%2Fgatsby_graphql_api%2Fgithub_graphql_explorer.gif" alt="Github GraphQL explorer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The nice thing with GraphiQL is that it gives you auto-complete. The docs on the upper-right corner are also super useful.&lt;/p&gt;
&lt;h2&gt;
  
  
  Query to get all the repo details we need
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;viewer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;repositories&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;updatedAt&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;forkCount&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;openGraphImageUrl&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;stargazers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;totalCount&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;readme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"master:README.md"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Blob&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;licenseInfo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;primaryLanguage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;languages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Which gives something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1595462513%2Fdevto%2Fgatsby_graphql_api%2Fgithub_graphql_query_get_repo_details.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1595462513%2Fdevto%2Fgatsby_graphql_api%2Fgithub_graphql_query_get_repo_details.png" alt="Get repo details in Github GraphQL explorer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'll let you explore the &lt;a href="https://developer.github.com/v4/object/repository/" rel="noopener noreferrer"&gt;Repository object&lt;/a&gt; to see details on the repo fields, but here are some notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;We got the README contents by using an expression and getting the text from the Blob returned. Details here &lt;a href="https://github.community/t/graphql-getting-filename-file-content-and-commit-date/13724/3" rel="noopener noreferrer"&gt;Github Community: GraphQL getting filename file content and commit date&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;openGraphImageUrl&lt;/code&gt; contains your repo's &lt;em&gt;Social Media Preview&lt;/em&gt;, which shows when you post your Github repo on Facebook, Twitter, blog, etc. This defaults to your Github profile photo, but it can be customized on the repo settings. Max 1MB for the photo. &lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1595462531%2Fdevto%2Fgatsby_graphql_api%2Frepo_settings_social_preview.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1595462531%2Fdevto%2Fgatsby_graphql_api%2Frepo_settings_social_preview.png" alt="Repo settings social preview"&gt;&lt;/a&gt; Photo by &lt;a href="https://unsplash.com/@christianw?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Christian Wiediger&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/computers?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cool! 😎&lt;/p&gt;

&lt;p&gt;For details on using the explorer:&lt;br&gt;
&lt;a href="https://developer.github.com/v4/guides/using-the-explorer/" rel="noopener noreferrer"&gt;Docs on Using Github GraphQL API explorer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"We can get the data, but how can we get this into our web app?"&lt;/em&gt; 🤷‍♀️&lt;/p&gt;
&lt;h1&gt;
  
  
  Github
&lt;/h1&gt;
&lt;h2&gt;
  
  
  Generate a personal access token
&lt;/h2&gt;

&lt;p&gt;A personal access token gives access to our app so it can make requests to our Github account:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;read repository details&lt;/li&gt;
&lt;li&gt;read user profile data&lt;/li&gt;
&lt;li&gt;create gists&lt;/li&gt;
&lt;li&gt;create and delete repositories&lt;/li&gt;
&lt;li&gt;read and write security keys&lt;/li&gt;
&lt;li&gt;etc&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To get a personal access token, you'll have to go to Developer Settings and generate one with the access scopes your app needs.&lt;/p&gt;

&lt;p&gt;You'll only need repo and user access for this exercise.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1595462532%2Fdevto%2Fgatsby_graphql_api%2Fgithub_get_personal_access_token.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1595462532%2Fdevto%2Fgatsby_graphql_api%2Fgithub_get_personal_access_token.gif" alt="Get personal access token from Github"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;⚠ Copy-paste the personal access token somewhere since it will disappear next time you go to this page! 😱&lt;/p&gt;
&lt;h1&gt;
  
  
  Gatsby - Node part
&lt;/h1&gt;
&lt;h2&gt;
  
  
  You'll need a Gatsby site 😅
&lt;/h2&gt;

&lt;p&gt;If you don't have one, you can use this &lt;a href="https://github.com/gatsbyjs/gatsby-starter-default" rel="noopener noreferrer"&gt;default starter&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gatsby new my-default-starter https://github.com/gatsbyjs/gatsby-starter-default
...
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;my-default-starter/
&lt;span class="nv"&gt;$ &lt;/span&gt;gatsby develop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;There's a lot more starters here to choose from. Take your pick at &lt;a href="https://www.gatsbyjs.org/starters" rel="noopener noreferrer"&gt;Gatsby starters&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Put the query somewhere in the "Node" part of Gatsby
&lt;/h2&gt;

&lt;p&gt;To keep organized, let's create a file &lt;code&gt;github-api.js&lt;/code&gt; where we can put our GraphQL query from before.&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;// github-api.js&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;githubApiQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
query($github_login: String!) {
    user(login: $github_login) {
      name
      repositories(first: 10) {
        nodes {
          ...
        }
      }
    }
  }
`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the query we used before to get repo details, but since we're not on our Github account's context anymore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;viewer&lt;/code&gt; is not available&lt;/li&gt;
&lt;li&gt;Github login of account to be queried must be specified&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  GraphQL variables
&lt;/h3&gt;

&lt;p&gt;A GraphQL variable &lt;code&gt;$github_login&lt;/code&gt; of type String:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;passed from the plugin config, and received by the query&lt;/li&gt;
&lt;li&gt;used to identify which Github &lt;code&gt;user&lt;/code&gt; to get info from, by &lt;code&gt;login&lt;/code&gt; name&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;We're using Node syntax since this will be &lt;code&gt;require&lt;/code&gt;d later in &lt;code&gt;gatsby-config.js&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Put personal Access Token in an .env 🔐
&lt;/h2&gt;

&lt;p&gt;Putting plaintext API keys in our config code is not secure! 🕵️‍♂️ &lt;/p&gt;

&lt;p&gt;It's such a bad idea that Github revoked my Personal Access Token as soon as I tried pushing it to a public repo! I had to get a new one 😏&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1595462512%2Fdevto%2Fgatsby_graphql_api%2Fgithub_email_personal_access_token_leaked.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1595462512%2Fdevto%2Fgatsby_graphql_api%2Fgithub_email_personal_access_token_leaked.png" alt="Github email Personal Access Token found in commit"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for forcing me to follow secure practices, Github! 🙏&lt;/p&gt;

&lt;p&gt;Let's use &lt;code&gt;dotenv&lt;/code&gt; library and put sensitive keys like this in a &lt;code&gt;.env&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;yarn add dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;.env&lt;/strong&gt; file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GITHUB_LOGIN=your_github_login
GITHUB_PERSONAL_ACCESS_TOKEN=your_github_personal_access_token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install and configure Gatsby Plugin for pulling data from Github GraphQL API
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;yarn add gatsby-source-github-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configure plugin in &lt;code&gt;gatsby-config.js&lt;/code&gt; with the query
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// gatsby-config.js&lt;/span&gt;

&lt;span class="c1"&gt;// init. environment variables&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dotenv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&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;githubApiQuery&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./github-api&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;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`gatsby-source-github-api`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.github.com/graphql&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// default Github GraphQL v4 API endpoint&lt;/span&gt;

      &lt;span class="c1"&gt;// token: required by the GitHub API&lt;/span&gt;
      &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GITHUB_PERSONAL_ACCESS_TOKEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

      &lt;span class="c1"&gt;// GraphQLquery: defaults to a search query&lt;/span&gt;
      &lt;span class="nx"&gt;graphQLQuery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;githubApiQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

      &lt;span class="c1"&gt;// variables: defaults to variables needed for a search query&lt;/span&gt;
      &lt;span class="nx"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;github_login&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GITHUB_LOGIN&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;ul&gt;
&lt;li&gt;Import the query from the module we created before&lt;/li&gt;
&lt;li&gt;Configure the plugin so it can connect to Github GraphQL API successfully&lt;/li&gt;
&lt;li&gt;Import Github credentials from &lt;code&gt;.env&lt;/code&gt;: &lt;code&gt;GITHUB_PERSONAL_ACCESS_TOKEN&lt;/code&gt; and &lt;code&gt;GITHUB_LOGIN&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Supply &lt;code&gt;github_login&lt;/code&gt; variable here, so the &lt;code&gt;$github_login&lt;/code&gt; variable in the query will have the value&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Start it up! 👩‍🚀👨‍🚀
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gatsby develop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that our data is available from the backend, let's use this on the frontend/UI side!&lt;/p&gt;

&lt;h1&gt;
  
  
  Gatsby - React part
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Formulate frontend query with local GraphiQL 🧪
&lt;/h2&gt;

&lt;p&gt;Fire up local GraphiQL &lt;a href="http://localhost:8000/___graphql" rel="noopener noreferrer"&gt;http://localhost:8000/___graphql&lt;/a&gt; to see our Github data&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1595462539%2Fdevto%2Fgatsby_graphql_api%2Flocal_graphiql_test_query.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1595462539%2Fdevto%2Fgatsby_graphql_api%2Flocal_graphiql_test_query.gif" alt="Local GraphiQL test query"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Soooo nice to use GraphiQL, isn't it? You can just click away to formulate your query.&lt;/p&gt;

&lt;p&gt;Copy-paste the resulting query on the "Query window" so we can use in our React component.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Page
&lt;/h2&gt;

&lt;p&gt;Create a page where you want to showcase your repos, like a Projects page.&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;// pages/projects.js&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Projects&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;Projects&lt;/span&gt; &lt;span class="nx"&gt;will&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt; &lt;span class="nx"&gt;here&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;code&gt;useStaticQuery&lt;/code&gt; hook for querying GraphQL data
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Static Query&lt;/strong&gt; queries GraphQL at build time. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The nice thing is that it can appear anywhere in the component tree (vs page query that has to be top-level page component)&lt;/li&gt;
&lt;li&gt;The nicer thing with the hooks version &lt;code&gt;useStaticQuery&lt;/code&gt; is that you don't need Render Props to use it. Just run it and use the &lt;code&gt;data&lt;/code&gt; result!&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;🔖 Github data only gets pulled once during build, since it's a Static Query after all. Meaning, it won't get latest updates from your Github, until site is rebuilt.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/projects.js&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useStaticQuery&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gatsby&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Projects&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useStaticQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;graphql&lt;/span&gt;&lt;span class="s2"&gt;`
      query MyQuery {
        allGithubData {
          nodes {
            data {
              user {
                repositories {
                  nodes {
                    description
                    forkCount
                    id
                    name
                    openGraphImageUrl
                    updatedAt(fromNow: true)
                    url
                    primaryLanguage {
                      name
                    }
                    languages {
                      nodes {
                        name
                      }
                    }
                    readme {
                      text
                    }
                    stargazers {
                      totalCount
                    }
                  }
                }
              }
            }
          }
        }
      }
    `&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;repos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allGithubData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repositories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nodes&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;repos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Projects&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Github&lt;/span&gt; &lt;span class="nx"&gt;Repos&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ul&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;repos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/li&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ul&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that our query pretty much reflects the query we passed to the plugin. The difference is that Gatsby gives us a bit more customizations for our frontend code.&lt;/p&gt;

&lt;p&gt;For example, for the &lt;code&gt;updatedAt&lt;/code&gt; field, we can upgrade from a boring timestamp "2020-07-16T02:06:57Z" to something like "updated 1 hour ago" with the use of &lt;code&gt;updatedAt(fromNow: true)&lt;/code&gt; 🙂&lt;/p&gt;

&lt;p&gt;Play around with your local GraphiQL (&lt;a href="http://localhost:8000/___graphql" rel="noopener noreferrer"&gt;http://localhost:8000/___graphql&lt;/a&gt;) to find out all these fanciness.✨&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1595462520%2Fdevto%2Fgatsby_graphql_api%2Fui_github_repos_preview.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1595462520%2Fdevto%2Fgatsby_graphql_api%2Fui_github_repos_preview.png" alt="UI Github Repos Preview"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1595462519%2Fdevto%2Fgatsby_graphql_api%2Fui_github_repos_preview_2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1595462519%2Fdevto%2Fgatsby_graphql_api%2Fui_github_repos_preview_2.png" alt="UI Github Repos Preview Console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Awesome! 🎉&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Our fictional github hero has forked a few popular repos for demo 🦸‍♀️🦸‍♂️&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now that we have all the data we need on the UI side, time to React! 🌀📏📐&lt;/p&gt;

&lt;h1&gt;
  
  
  React component
&lt;/h1&gt;

&lt;p&gt;Some highlights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;parse README.md using &lt;code&gt;react-markdown&lt;/code&gt;, make it scrollable so it doesn't fill the page with one repo's README&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Social Media Preview&lt;/em&gt; image (&lt;code&gt;openGraphImageUrl&lt;/code&gt;) on the right side&lt;/li&gt;
&lt;li&gt;liberal use of flexbox 😁&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can view the Projects view implementation here:&lt;br&gt;
&lt;a href="https://github.com/lenmorld/gatsby-default-starter-with-github-repos/blob/master/src/pages/index.js" rel="noopener noreferrer"&gt;Projects vies&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voila! 💥&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1595462572%2Fdevto%2Fgatsby_graphql_api%2Fgithub_repos_in_gatsby.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1595462572%2Fdevto%2Fgatsby_graphql_api%2Fgithub_repos_in_gatsby.gif" alt="Github repos in Gatsby"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  BONUS: Deploy in Netlify
&lt;/h1&gt;

&lt;p&gt;After you built this into your Gatsby site and pushed the code to a Github repo...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is so meta! A Github repo about getting Github repos 🥴🤯. &lt;a href="https://i.giphy.com/media/yAOjunY81Trjy/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/yAOjunY81Trjy/giphy.gif" alt="Its so meta!"&gt;&lt;/a&gt;&lt;/p&gt;
Gif from &lt;a href="https://giphy.com/gifs/computer-infinite-endless-yAOjunY81Trjy" rel="noopener noreferrer"&gt;Giphy - computer infinite endless&lt;/a&gt;


&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Create a new site from your Gatsby site repo
&lt;/h2&gt;

&lt;p&gt;From your Netlify dashboard, create new site and follow steps to create one from a Github repo&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1595462518%2Fdevto%2Fgatsby_graphql_api%2Fnetlify_config_1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1595462518%2Fdevto%2Fgatsby_graphql_api%2Fnetlify_config_1.png" alt="Netlify config 1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The nice thing is that you can specify the environment variables before deploy. 🤩&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Remember that we can't commit &lt;code&gt;.env&lt;/code&gt; to remote since that would expose our secrets to the cloud. 🤔 So we have to configure the env. variable directly on our cloud provider, which is Netlify in this case&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1595462518%2Fdevto%2Fgatsby_graphql_api%2Fnetlify_config_2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdvfhgkkpe%2Fimage%2Fupload%2Fv1595462518%2Fdevto%2Fgatsby_graphql_api%2Fnetlify_config_2.png" alt="Netlify config 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Deploy! 🚀&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wizardly-yonath-ca537a.netlify.app/" rel="noopener noreferrer"&gt;Live demo&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Code
&lt;/h1&gt;

&lt;p&gt;Full implementation here built on top of &lt;code&gt;gatsby-default-starter&lt;/code&gt;:&lt;br&gt;
&lt;a href="https://github.com/lenmorld/gatsby-default-starter-with-github-repos" rel="noopener noreferrer"&gt;Full Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Catch you in the next one! Have fun GraphQL-ing! 🙋‍♀️🙋‍♂️&lt;/p&gt;

</description>
      <category>gatsby</category>
      <category>graphql</category>
      <category>react</category>
      <category>github</category>
    </item>
    <item>
      <title>Node Workshop - free online workshop by SCS Concordia</title>
      <dc:creator>Lenmor Ld</dc:creator>
      <pubDate>Fri, 10 Jul 2020 00:24:31 +0000</pubDate>
      <link>https://forem.com/lennythedev/node-workshop-free-online-workshop-by-scs-concordia-3dc5</link>
      <guid>https://forem.com/lennythedev/node-workshop-free-online-workshop-by-scs-concordia-3dc5</guid>
      <description>&lt;p&gt;Come learn about web technologies at this Node.js workshop! &lt;/p&gt;

&lt;p&gt;This workshop will cover code and concepts to help you build a solid foundation for your next web project! We'll ALSO be giving out FREE UBER EATS GIFT CARDS to attendees courtesy of our sponsor, Autodesk!&lt;/p&gt;

&lt;h3&gt;
  
  
  Workshop happening at July 16 (Thursday) 17:30 EST
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Sign up through Facebook events: &lt;a href="https://www.facebook.com/events/1125391177830448" rel="noopener noreferrer"&gt;https://www.facebook.com/events/1125391177830448&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;or sign up directly through this Google Form:&lt;br&gt;
&lt;a href="https://forms.gle/wabMEtUXMW8f9Mj76" rel="noopener noreferrer"&gt;https://forms.gle/wabMEtUXMW8f9Mj76&lt;/a&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This workshop focuses on Web servers so come and learn how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use Node and Express to build a web server and REST API&lt;/li&gt;
&lt;li&gt;understand routing, request and response&lt;/li&gt;
&lt;li&gt;implement CRUD with HTTP methods&lt;/li&gt;
&lt;li&gt;build a server-rendered website using templates&lt;/li&gt;
&lt;li&gt;connect to a Cloud NoSQL database: MongoDB Atlas DB&lt;/li&gt;
&lt;li&gt;user authentication with sessions, cookies and tokens&lt;/li&gt;
&lt;li&gt;using external APIs, such as Github Jobs, Giphy, Spotify and more!&lt;/li&gt;
&lt;/ul&gt;

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

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

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

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

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

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

&lt;p&gt;Here's a quick preview of what you'll be working on and some prior reading to cover:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.google.com/presentation/d/1TjA9uz7fkg7JLzlywe07J0wcC2car4sn60GkI9XsdTg/edit?usp=sharing" rel="noopener noreferrer"&gt;Workshop preview in Google slides&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;*Note: This is an online event that will be conducted live on YouTube. The link will be provided prior to the event.&lt;/p&gt;

&lt;p&gt;See you there! 🤓 &lt;br&gt;
👩‍💻 👨‍💻&lt;/p&gt;

</description>
      <category>node</category>
      <category>mongodb</category>
      <category>beginners</category>
      <category>workshop</category>
    </item>
    <item>
      <title>CSS gotcha: How to fill page with a div?</title>
      <dc:creator>Lenmor Ld</dc:creator>
      <pubDate>Sun, 12 Jan 2020 21:57:05 +0000</pubDate>
      <link>https://forem.com/lennythedev/css-gotcha-how-to-fill-page-with-a-div-270j</link>
      <guid>https://forem.com/lennythedev/css-gotcha-how-to-fill-page-with-a-div-270j</guid>
      <description>&lt;h3&gt;
  
  
  TL;DR
&lt;/h3&gt;

&lt;p&gt;If you want to have a full-page container div, make sure you have these:&lt;/p&gt;

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

&lt;span class="c"&gt;/* override browser default */&lt;/span&gt;
&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* use viewport-relative units to cover page fully */&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100vw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* include border and padding in element width and height */&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* full-sized  container that fills up the page */&lt;/span&gt;
&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c"&gt;/* example padding, font-size, background, etc */&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;lightskyblue&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;
  
  
  So let's say you want a div that fills up entire page...
&lt;/h2&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;

&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;lightskyblue&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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F50lpa67m5v4s332x6xmz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F50lpa67m5v4s332x6xmz.png" alt="./Untitled.png" width="401" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What?! It doesn't work! The height still only takes up the content, but not the whole page.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The width is good since a div is by default a &lt;strong&gt;block element&lt;/strong&gt;, which takes as much width as possible anyways.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Can we just use a more "absolute" value like &lt;code&gt;px&lt;/code&gt; ?
&lt;/h2&gt;

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

&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* height: 100% */&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;865px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* current height of my browser */&lt;/span&gt;
  &lt;span class="c"&gt;/* ... */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;It works... until the browser is resized&lt;/p&gt;

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

&lt;p&gt;It doesn't adapt when the browser is resized.&lt;/p&gt;

&lt;p&gt;You can use JS for this, but that's way overkill for what we wanted.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I mentioned &lt;code&gt;px&lt;/code&gt; is "absolute", but only in the sense that they are not relative to anything else (like rem and vh). But the actual size still depends on the device. Here's some details:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/40480617/is-a-css-pixel-really-an-absolute-unit-that-is-is-1-inch-96px-true" rel="noopener noreferrer"&gt;Stack Overflow: Is a CSS pixel really an absolute unit?&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Relative units to the rescue!
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Old school &lt;code&gt;height: 100%&lt;/code&gt;
&lt;/h3&gt;

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

&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft8qxw4gn7hndo5nmqgiz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft8qxw4gn7hndo5nmqgiz.png" width="473" height="229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Works! &lt;em&gt;(We'll fix the scrollbars later)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;By setting both &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; and its child &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; to 100% height, we achieve the full size.&lt;/p&gt;

&lt;p&gt;Note that only setting either of them won't work, since &lt;strong&gt;percentage is always relative to another value.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;div&lt;/code&gt; is 100% the height of the &lt;code&gt;body&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;body&lt;/code&gt; is 100% the height of the &lt;code&gt;html&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;html&lt;/code&gt; is 100% the height of the Viewport&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Viewport&lt;/strong&gt; is the visible area of the browser, which varies by device.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Viewport &amp;gt; &lt;code&gt;html&lt;/code&gt; &amp;gt; &lt;code&gt;body&lt;/code&gt; &amp;gt; &lt;code&gt;div&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example, an iPhone 6/7/8 has a 375x667 viewport. You can verify this on your browser dev tools mobile options.&lt;/p&gt;

&lt;p&gt;For now, you can think about viewport as the device pixel size or resolution. But if you want to go deep:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mediag.com/blog/popular-screen-resolutions-designing-for-all/" rel="noopener noreferrer"&gt;Media Genesis: Screen Size, Resolution, and Viewport: What does it all mean?&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  newer solution: viewport units &lt;code&gt;vh&lt;/code&gt; and &lt;code&gt;vw&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Viewport-percentage lengths&lt;/em&gt; aka &lt;strong&gt;Viewport units&lt;/strong&gt; have been around for a while now, and is perfect for responding to browser resizes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;1 viewport height (&lt;code&gt;1vh&lt;/code&gt;) = 1% of viewport height&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;1 viewport width (&lt;code&gt;1vw&lt;/code&gt;) = 1% of viewport width&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words, &lt;code&gt;100vh&lt;/code&gt; = 100% of the viewport height&lt;/p&gt;

&lt;p&gt;&lt;code&gt;100vw&lt;/code&gt; = 100% of the viewport width&lt;/p&gt;

&lt;p&gt;So these effectively fills up the device viewport.&lt;/p&gt;

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

&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* height: 100%; */&lt;/span&gt;
  &lt;span class="c"&gt;/* width: 100% */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* height: 100%; 
        width: 100%; */&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100vw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft8qxw4gn7hndo5nmqgiz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft8qxw4gn7hndo5nmqgiz.png" width="473" height="229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looks good too! &lt;em&gt;(We'll fix the scrollbars later)&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As mentioned in the comments by &lt;a class="mentioned-user" href="https://dev.to/angelsixuk"&gt;@angelsixuk&lt;/a&gt; and &lt;a class="mentioned-user" href="https://dev.to/mpuckett"&gt;@mpuckett&lt;/a&gt;, there is a known &lt;em&gt;jumping behavior during scrolling&lt;/em&gt; when using &lt;code&gt;100vh&lt;/code&gt; on mobile browsers, which is an issue but considered intentional by webkit. See these links for details: &lt;a href="https://nicolas-hoizey.com/articles/2015/02/viewport-height-is-taller-than-the-visible-part-of-the-document-in-some-mobile-browsers/" rel="noopener noreferrer"&gt;Viewport height is taller than the visible part of the document in some mobile browsers&lt;/a&gt; and &lt;a href="https://stackoverflow.com/questions/37112218/css3-100vh-not-constant-in-mobile-browser" rel="noopener noreferrer"&gt;Stack Overflow: CSS3 100vh not constant in mobile browser&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  How about &lt;code&gt;min-height: 100vh&lt;/code&gt; ?
&lt;/h3&gt;

&lt;p&gt;While &lt;code&gt;height&lt;/code&gt; fixes the length at &lt;code&gt;100vh&lt;/code&gt;, &lt;code&gt;min-height&lt;/code&gt; starts at &lt;code&gt;100vh&lt;/code&gt; but allows content to extend the div beyond that length. If content is less than the length specified, &lt;code&gt;min-height&lt;/code&gt; has no effect.&lt;/p&gt;

&lt;p&gt;In other words, &lt;code&gt;min-height&lt;/code&gt; makes sure the element is &lt;strong&gt;at least&lt;/strong&gt; that length, and overrides &lt;code&gt;height&lt;/code&gt; if &lt;code&gt;height&lt;/code&gt; is defined and smaller than &lt;code&gt;min-height&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For our goal of having a div child with full height and width, it doesn't make any difference since the content is also at full size.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A good use case of &lt;code&gt;min-height&lt;/code&gt; is for having a sticky footer that gets pushed when there is more content on the page. Check this out here and other good uses of vh&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://css-tricks.com/fun-viewport-units/#article-header-id-2" rel="noopener noreferrer"&gt;Fun with Viewport Units | CSS-Tricks&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;A very common practice is to apply &lt;code&gt;height: 100vh&lt;/code&gt; and &lt;code&gt;width: 100vw&lt;/code&gt; to &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; directly...&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In this case, we can even keep the container &lt;code&gt;div&lt;/code&gt; relatively sized like in the beginning, in case we change our minds later.&lt;/p&gt;

&lt;p&gt;And with this approach, we assure that our entire DOM body occupies full height and width regardless of our container div.&lt;/p&gt;

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

&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100vw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&gt;/* height: 100vh;
  width: 100vw; */&lt;/span&gt;
  &lt;span class="c"&gt;/* ... */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;vh/vw&lt;/code&gt; versus &lt;code&gt;%&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A good way of thinking about &lt;code&gt;vh, vw&lt;/code&gt; vs &lt;code&gt;%&lt;/code&gt; is that they are analogous to &lt;code&gt;em&lt;/code&gt; and &lt;code&gt;rem&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;%&lt;/code&gt; and &lt;code&gt;em&lt;/code&gt; are both relative to the parent size, while &lt;code&gt;vw/vh&lt;/code&gt; and &lt;code&gt;rem&lt;/code&gt; are both relative to "the highest reference", root font size for rem and device viewport for vh/vw.&lt;/p&gt;
&lt;h2&gt;
  
  
  But why the scrollbar?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj1pfvxg37697jub4a0aj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj1pfvxg37697jub4a0aj.png" width="474" height="230"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; have default margins and paddings!
&lt;/h2&gt;

&lt;p&gt;Browsers feature a default margin, padding and borders to HTML elements. And the worst part is it's different for each browser!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chrome default for &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; has a &lt;code&gt;margin: 8px&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And &lt;code&gt;100vh + 8px&lt;/code&gt; causes an overflow, since it's more than the viewport&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Luckily, it's fairly easy to fix that:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;

&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This is a "blanket" solution that would cover all margin and padding variations for any browser you might have.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cool! Now we have our div filling up the page without scrollbars!
&lt;/h2&gt;

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

&lt;p&gt;Finally, let's add a little padding, since it's awkward that the content is right on the edges.&lt;/p&gt;

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

&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&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;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhk98jyrxdkwbn45u3b56.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhk98jyrxdkwbn45u3b56.png" width="475" height="227"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What?! The scrollbar is back! What happened?&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;box-sizing&lt;/code&gt; border-box
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;box-sizing&lt;/code&gt; allows you to define whether the &lt;strong&gt;padding and border&lt;/strong&gt; is included in the div's height and width.&lt;/p&gt;

&lt;p&gt;The default &lt;code&gt;content-box&lt;/code&gt; of &lt;code&gt;box-sizing&lt;/code&gt; doesn't include padding and border in the length, so div becomes&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;height = 100% + 10px * 2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;width = 100% + 10px * 2&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;which overflows the page!&lt;/p&gt;

&lt;p&gt;&lt;code&gt;border-box&lt;/code&gt; includes padding and border, so div stays at our required sizes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;height = 100%&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;width = 100%&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's quite common to set &lt;strong&gt;all&lt;/strong&gt; elements to &lt;code&gt;border-box&lt;/code&gt; for a consistent layout and sizing throughout pages, using &lt;code&gt;*&lt;/code&gt; selector:&lt;/p&gt;


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

&lt;p&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
  &lt;span class="nl"&gt;box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Perfect!&lt;br&gt;
&lt;/h3&gt;

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

&lt;h3&gt;
  
  
  Catch you in the next one!
&lt;/h3&gt;

</description>
      <category>todayilearned</category>
      <category>beginners</category>
      <category>css</category>
    </item>
    <item>
      <title>Why does the dignified primitive hate the day after Christmas?</title>
      <dc:creator>Lenmor Ld</dc:creator>
      <pubDate>Thu, 26 Dec 2019 14:32:23 +0000</pubDate>
      <link>https://forem.com/lennythedev/why-does-the-dignified-primitive-hates-the-day-after-christmas-20fn</link>
      <guid>https://forem.com/lennythedev/why-does-the-dignified-primitive-hates-the-day-after-christmas-20fn</guid>
      <description>&lt;p&gt;It's &lt;strong&gt;"Boxing" day&lt;/strong&gt;, ...&lt;br&gt;
It doesn't want to be &lt;strong&gt;Object-ified&lt;/strong&gt;! &lt;br&gt;
📦&lt;/p&gt;

&lt;p&gt;🤭&lt;/p&gt;

</description>
      <category>jokes</category>
      <category>humor</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Ruby gotchas Part 1</title>
      <dc:creator>Lenmor Ld</dc:creator>
      <pubDate>Fri, 20 Dec 2019 14:29:41 +0000</pubDate>
      <link>https://forem.com/lennythedev/ruby-gotchas-part-1-d41</link>
      <guid>https://forem.com/lennythedev/ruby-gotchas-part-1-d41</guid>
      <description>&lt;p&gt;Feel free to skip this if you eat &lt;em&gt;gems&lt;/em&gt; 💎 for breakfast or you take &lt;em&gt;rails&lt;/em&gt; 🛤️ everyday.&lt;/p&gt;

&lt;p&gt;🥁🥁 (Ba dum tss)&lt;/p&gt;

&lt;p&gt;Here's some tidbits of information and "gotchas" that I had learning Ruby, as someone coming from JS-land.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NOTE: I'm using irb with Ruby 2.6.5&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1.✨ Ruby is dynamically-typed like JS
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Dynamic&lt;/em&gt; means the type of the variable is resolved on-the-fly and can be changed at run time (hence, dynamic). &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;compared to static typed like Java or TypeScript&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No need for types when declaring variable, and the value assigned to variable can be of any type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;me&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;  &lt;span class="c1"&gt;# int&lt;/span&gt;
&lt;span class="n"&gt;me&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"I'm dynamic"&lt;/span&gt;  &lt;span class="c1"&gt;# string&lt;/span&gt;
&lt;span class="n"&gt;me&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:a&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"fancy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:b&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;# object&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  2.🔢 Integer() vs &lt;code&gt;to_i&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Integer()&lt;/code&gt; errors out with non-Integer strings&lt;/p&gt;

&lt;p&gt;while &lt;code&gt;to_i&lt;/code&gt; tries really hard, and spits out 0 if it can't&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="s2"&gt;"a1"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="no"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"a1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invalid&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="no"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="s2"&gt;"a1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Same with &lt;code&gt;Float(str)&lt;/code&gt; and &lt;code&gt;str.to_f&lt;/code&gt; as well&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;floaty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"3.14"&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;floaty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_f&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;3.15&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Converting from other bases&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The nice thing with &lt;code&gt;Integer(str, radix)&lt;/code&gt; is that you can also use it to convert other bases to integer, just like &lt;code&gt;parseInt()&lt;/code&gt; in JS.&lt;/p&gt;

&lt;p&gt;E.g. if you want to convert binary to integer&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"101"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  3.📦 Everything is an &lt;strong&gt;object&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Just like in Python, but not exactly like Javascript (un-boxed primitives like strings, numbers are not objects).&lt;/p&gt;

&lt;p&gt;You can use &lt;code&gt;"me".methods&lt;/code&gt; to see all supported methods of String &lt;code&gt;"me"&lt;/code&gt; and Integer &lt;code&gt;123&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="s2"&gt;"me"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;methods&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"upcase!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"empty?, "&lt;/span&gt;&lt;span class="n"&gt;to_f&lt;/span&gt;&lt;span class="s2"&gt;", "&lt;/span&gt;&lt;span class="n"&gt;to_i&lt;/span&gt;&lt;span class="s2"&gt;", "&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="s2"&gt;",...]

123.methods
=&amp;gt; ["&lt;/span&gt;&lt;span class="nb"&gt;to_s&lt;/span&gt;&lt;span class="s2"&gt;", "&lt;/span&gt;&lt;span class="n"&gt;odd?&lt;/span&gt;&lt;span class="s2"&gt;", "&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;even?&lt;/span&gt;&lt;span class="s2"&gt;", ...]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  4.🦆 Duck-typing  and &lt;code&gt;respond_to?&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Duck&lt;/em&gt;-typed means an object is considered a "Duck" if it has methods of a duck: it walks like a duck, it quacks like a duck. &lt;/p&gt;

&lt;p&gt;For example, if 123 has &lt;code&gt;odd?&lt;/code&gt; and &lt;code&gt;even?&lt;/code&gt; methods, then it must be an Integer?! Use &lt;code&gt;respond_to?&lt;/code&gt; to check whether an object is able to do / has something, instead of using &lt;code&gt;.methods&lt;/code&gt; everytime&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond_to?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"odd?"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  5.🎚️ Ranges &lt;code&gt;(1..5)&lt;/code&gt; and using them in loops
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;Range&lt;/code&gt; is a sequence of values that can be used as a collection or converted easily to an array using &lt;code&gt;to_a&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;..&lt;/code&gt; for including high value and &lt;code&gt;...&lt;/code&gt; for excluding it&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use as a collection for iterating&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;Enumerator: 1..2:each&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🔖 &lt;code&gt;print&lt;/code&gt; doesn't include a newline like &lt;code&gt;puts&lt;/code&gt; does&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  ⚠️ Don't use for loops!
&lt;/h3&gt;

&lt;p&gt;You can also use ranges in loops, but note that it is not common and iterators should be used most of the time.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For loops are also considered bad in the &lt;strong&gt;Rubocop style guide&lt;/strong&gt;, since the variables defined inside it leaks outside of the loop&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="err"&gt;❌&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;12345&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; 
&lt;span class="err"&gt;😱&lt;/span&gt;

&lt;span class="err"&gt;✔️&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;NameError&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;undefined&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt; &lt;span class="n"&gt;variable&lt;/span&gt; &lt;span class="n"&gt;or&lt;/span&gt; &lt;span class="nb"&gt;method&lt;/span&gt; &lt;span class="sb"&gt;`i')
😎
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;For more info: &lt;a href="https://rubystyle.guide/#no-for-loops"&gt;https://rubystyle.guide/#no-for-loops&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use as an array&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Works in characters too!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'1x'&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="s1"&gt;'2b'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"1x"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"1y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"1z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"2a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"2b"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And you can check if an item is inside a range&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'a'&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="s1"&gt;'e'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt; &lt;span class="s1"&gt;'b'&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'a'&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="s1"&gt;'e'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt; &lt;span class="s1"&gt;'z'&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;






&lt;p&gt;That's all for now. 😄&lt;/p&gt;

&lt;p&gt;I'm not a Ruby ninja 🤺 (yet),&lt;br&gt;
and these are mostly observations for me. &lt;br&gt;
So let me know of any corrections and improvements in the comments. 👇&lt;/p&gt;

&lt;p&gt;Happy Ruby-ing! 💎&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>ruby</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>🗣 Web Reader using Web Speech API</title>
      <dc:creator>Lenmor Ld</dc:creator>
      <pubDate>Sat, 14 Dec 2019 15:21:22 +0000</pubDate>
      <link>https://forem.com/lennythedev/web-reader-using-web-speech-api-bkn</link>
      <guid>https://forem.com/lennythedev/web-reader-using-web-speech-api-bkn</guid>
      <description>&lt;p&gt;Demo here: &lt;a href="https://stupefied-curran-2254b8.netlify.com/" rel="noopener noreferrer"&gt;https://stupefied-curran-2254b8.netlify.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have you ever been TLDR (Too LAZY didn't read) to read an online article or any webpage of some sort...&lt;br&gt;
and wished that your &lt;strong&gt;browser would read it for you&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;Well, you're in luck! I built a Web Page Reader. 😆&lt;br&gt;
Just copy-paste a URL or some text in the input and it would read it for you! &lt;br&gt;
&lt;em&gt;Well, the readable parts at least&lt;/em&gt; 😅&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/cnoAIZkiGPbHJuvzLa/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/cnoAIZkiGPbHJuvzLa/giphy.gif" alt="start talking"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  💬 Web Speech API
&lt;/h2&gt;

&lt;p&gt;I used &lt;strong&gt;Speech Synthesis&lt;/strong&gt; from native browser's Web Speech API.&lt;br&gt;
It is an experimental tech, but good chance you have this in your browser now!&lt;/p&gt;

&lt;p&gt;Actually, we all had this since Chrome 33, Firefox 49, Edge 14. But check here in case you are using a tamagochi 🐰: &lt;a href="https://caniuse.com/#search=web%20speech%20api" rel="noopener noreferrer"&gt;caniuse Web Speech API&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  The speech inputs
&lt;/h3&gt;

&lt;p&gt;User inputs are the following HTML elements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;textarea&lt;/code&gt; for the &lt;strong&gt;URL/text to read&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;select&lt;/code&gt; input for the &lt;strong&gt;voice&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;range&lt;/code&gt; inputs for &lt;strong&gt;pitch&lt;/strong&gt; and &lt;strong&gt;rate&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The textarea contents are checked if it's a plain text or a URL.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;rate&lt;/strong&gt; (how fast the speaking goes) ranges from 0.5 to 2. &lt;br&gt;
The &lt;strong&gt;pitch&lt;/strong&gt; (highness or lowness of voice) ranges from 0 to 2.&lt;br&gt;
The &lt;strong&gt;voice&lt;/strong&gt; select provides the voices available from the system.&lt;/p&gt;
&lt;h3&gt;
  
  
  🎤 &lt;code&gt;SpeechSynthesisVoice&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The voices available differs for every device, and is obtained via &lt;br&gt;
&lt;code&gt;speechSynthesisInstance.getVoices()&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;This returns all the &lt;code&gt;SpeechSynthesisVoice&lt;/code&gt; objects, which we stuff on the select options.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fk0d5yw25ouut1h5w7uu9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fk0d5yw25ouut1h5w7uu9.png" alt="Voice selection"&gt;&lt;/a&gt;&lt;br&gt;
User selects one of this, or leave the default.&lt;/p&gt;

&lt;p&gt;Now, what makes the browser actually talk is the &lt;code&gt;SpeechSynthesisUtterance&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/l1KuihAZXiLNzkF4Q/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/l1KuihAZXiLNzkF4Q/giphy.gif" alt="computer talking"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  🗣 &lt;code&gt;SpeechSynthesisUtterance&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;SpeechSynthesisUtterance&lt;/code&gt; object (&lt;code&gt;utterance&lt;/code&gt;) is like an individual speech request, which we initialize with the string and attach all the speech elements like voice, rate and pitch.&lt;/p&gt;

&lt;p&gt;Finally, trigger the utterance via &lt;code&gt;speechSynthesis.speak()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;finishUtteranceCallback&lt;/code&gt; is also supplied to enable play button and other controls when the text is finished.&lt;/p&gt;

&lt;p&gt;This logic is encapsulated in &lt;strong&gt;&lt;code&gt;speak(string, voice, pitch, rate, finishUtteranceCallback)&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nf"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;voice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pitch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;finishUtteranceCallback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;synth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speaking&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🗣 already speaking&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&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;utterance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SpeechSynthesisUtterance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;utterance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utterance end&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nf"&gt;finishUtteranceCallback&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="nx"&gt;utterance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;voice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;voice&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;utterance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pitch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pitch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;utterance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;synth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;utterance&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;All of this functionality is wrapped in a &lt;code&gt;WebSpeechApi&lt;/code&gt; to keep it modular. 📦&lt;/p&gt;

&lt;p&gt;For a detailed look at Speech Utterance, check this out: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisUtterance" rel="noopener noreferrer"&gt;MDN Speech Utterance&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis" rel="noopener noreferrer"&gt;MDN page&lt;/a&gt; has an awesome rundown and example where I built my app off of. Please check it out too!&lt;/p&gt;
&lt;h2&gt;
  
  
  🌐 URL check
&lt;/h2&gt;

&lt;p&gt;User can input URL or text on the &lt;code&gt;textarea&lt;/code&gt; to read.&lt;br&gt;
But how does this detect if it's a URL?&lt;br&gt;
A simple &lt;code&gt;try-catch&lt;/code&gt; does the trick.&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;// simple check if valid URL&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;urlOrText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;isUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// not a URL, treat as string&lt;/span&gt;
    &lt;span class="nx"&gt;isUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If it's a plain text, this is passed directly to the &lt;code&gt;speak()&lt;/code&gt;.&lt;br&gt;
If it is indeed a URL, GET request to load the page and scrape the readable elements.&lt;/p&gt;
&lt;h2&gt;
  
  
  🕷️ Web page scraping using &lt;code&gt;cheerio&lt;/code&gt; and &lt;code&gt;axios&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;cheerio&lt;/code&gt; is a subset of jQuery that is super fast, easy and flexible for parsing HTML.&lt;br&gt;
(Seriously it's as easy as &lt;code&gt;cheerio.load(&amp;lt;p&amp;gt;some html&amp;lt;/p&amp;gt;)&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;axios&lt;/code&gt; is a Promise-based client for fetching stuff from APIs, and in this case, getting the full HTTP get response from a webpage.&lt;/p&gt;

&lt;p&gt;Combined, this is how I'm getting all the "readable" elements of a page.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getWebsiteTexts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;siteUrl&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&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;axios&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;siteUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cheerio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&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;p, h1, h2, h3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// get all "readable" element contents&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;texts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;contents&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toArray&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&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="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&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="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;texts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// handle err&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;errorObj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toJSON&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;errorObj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; on &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;errorObj&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;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\nPlease try a different website`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;urlOrTextInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nf"&gt;finishUtterance&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;Some URLs error out so we catch the error, &lt;code&gt;alert()&lt;/code&gt; user, clear the textarea and reset the form inputs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why some URL doesn't work?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/3o6wO1GP7WCPaltkw8/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3o6wO1GP7WCPaltkw8/giphy.gif" alt="computer cry"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  ⛔ CORS policy
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The scraper can't parse all websites out there&lt;/strong&gt;.&lt;br&gt;
In fact, a lot of websites (try Medium articles) has a CORS policy.&lt;br&gt;
So you'll get an error like this in some websites.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fjmzlzp5qg519u6jambvt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fjmzlzp5qg519u6jambvt.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
&lt;code&gt;CORS policy: No 'Access-Control-Allow-Origin'&lt;/code&gt; means only the &lt;em&gt;Same Origin&lt;/em&gt; can do GET requests from a webapp script. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Note that &lt;code&gt;cURL&lt;/code&gt; and Postman may still work on these sites, just not from Javascript like this.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is enabled from the server of the site we're trying to read, so nothing much we can do but move on to a different page. 😢&lt;/p&gt;

&lt;p&gt;Here's a good rundown of CORS:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/g33konaut" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F34817%2F373d1b7e-fc82-4763-adf5-572c5754d6b5.jpg" alt="g33konaut"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/g33konaut/understanding-cors-aaf" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Understanding CORS&lt;/h2&gt;
      &lt;h3&gt;Martin Splitt ・ Nov 12 '19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#cors&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#security&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;dev.to&lt;/strong&gt; pages work though! Try it out 🎉. &lt;br&gt;
&lt;strong&gt;Thank you dev.to for allowing us to scrape&lt;/strong&gt; 🙏&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  ▶️ play, pause, restart
&lt;/h2&gt;

&lt;p&gt;Lastly, I added some basic playback control.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fq5xj948ja4t7bagflk8m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fq5xj948ja4t7bagflk8m.png" alt="Playback control"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's the &lt;em&gt;play&lt;/em&gt; function that starts or resume based on current &lt;code&gt;paused&lt;/code&gt; status of the &lt;code&gt;speechSyntesis&lt;/code&gt;. The other controls are just &lt;code&gt;disabled&lt;/code&gt; except &lt;em&gt;pause&lt;/em&gt; and &lt;em&gt;stop&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;playButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;speechApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;synth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paused&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;speechApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;synth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// start from beginning&lt;/span&gt;
    &lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;playButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;pauseButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;stopButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;rateSlider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;pitchSlider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;voiceSelect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;urlOrTextInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;em&gt;pause&lt;/em&gt; and &lt;em&gt;stop&lt;/em&gt; are more or less similar with different controls disabled.&lt;/p&gt;
&lt;h2&gt;
  
  
  📦 🚤 Build and Deployment
&lt;/h2&gt;

&lt;p&gt;I used &lt;code&gt;parcel&lt;/code&gt; for hassle-free no-config bundling, which is quite simple for vanilla JS projects like this. &lt;/p&gt;

&lt;p&gt;Lastly, Netlify for easy static deploy. After setting up the Github repo in Netlify, it just picked up the &lt;code&gt;dist/&lt;/code&gt; folder built by Parcel.&lt;/p&gt;

&lt;p&gt;Done!&lt;/p&gt;
&lt;h2&gt;
  
  
  📃 Improvements
&lt;/h2&gt;

&lt;p&gt;This is a quick project, so it could definitely use some improvements (and corrections).&lt;/p&gt;

&lt;p&gt;👨‍💻 Here's the code. Hope this spark some ideas and help yo get started with some awesome text-to-speech projects. 😁&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/lenmorld" rel="noopener noreferrer"&gt;
        lenmorld
      &lt;/a&gt; / &lt;a href="https://github.com/lenmorld/web_reader" rel="noopener noreferrer"&gt;
        web_reader
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Web Reader using Web Speech API&lt;/h1&gt;

&lt;/div&gt;

&lt;p&gt;Live demo at &lt;a href="https://stupefied-curran-2254b8.netlify.com/" rel="nofollow noopener noreferrer"&gt;https://stupefied-curran-2254b8.netlify.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/lenmorld/web_readerscreens/2019-12-14-10-23-53.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Flenmorld%2Fweb_readerscreens%2F2019-12-14-10-23-53.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Article at dev.to&lt;/h1&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://dev.to/lennythedev/web-reader-using-web-speech-api-bkn" rel="nofollow"&gt;https://dev.to/lennythedev/web-reader-using-web-speech-api-bkn&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;dev&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;npm run dev&lt;/code&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;build&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;npm run build&lt;/code&gt;&lt;/p&gt;

&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/lenmorld/web_reader" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;Any suggestions, comments, questions?&lt;br&gt;
(like on a better way to check if string is a URL 😅 )&lt;br&gt;
&lt;strong&gt;&lt;em&gt;Please let me know in the comments!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Thanks and happy listen-reading! 👂📖&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>texttospeech</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>🌱 Complete REST API with MongoDB Atlas cloud, Node, and Express in 10 minutes</title>
      <dc:creator>Lenmor Ld</dc:creator>
      <pubDate>Sat, 07 Dec 2019 19:00:41 +0000</pubDate>
      <link>https://forem.com/lennythedev/rest-api-with-mongodb-atlas-cloud-node-and-express-in-10-minutes-2ii1</link>
      <guid>https://forem.com/lennythedev/rest-api-with-mongodb-atlas-cloud-node-and-express-in-10-minutes-2ii1</guid>
      <description>&lt;p&gt;Let's bring the data to the cloud this time! 💾 ☁.&lt;br&gt;
And what better way to do that than a &lt;strong&gt;FREE&lt;/strong&gt; cloud database! &lt;br&gt;
We'll be using the free cluster of &lt;strong&gt;Mongo DB Atlas&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  What is Mongo DB Atlas?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Mongo DB Atlas is a fully-managed database-as-a-service available on AWS, Azure, and GCP&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before Atlas, I used &lt;strong&gt;mLab&lt;/strong&gt;. mLab was then acquired by MongoDB last year. Pretty much similar to mLab, MongoDB Atlas is the easiest way to have a cloud MongoDB instance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;💲 &lt;strong&gt;0 dollars&lt;/strong&gt;: I am not an advertiser for MongoDB, but I love this service and this one won't break the bank ❌🔨🐖, since... well it's no-strings-attached FREE for up to 512MB. Perfect for small side-projects!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🏃‍♂️ &lt;strong&gt;Quick, simple, and fast&lt;/strong&gt;: no installation and config files. After signing up and creating a cluster (takes 5 minutes), you're all set.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;☁ &lt;strong&gt;It's in the cloud&lt;/strong&gt;: Not running in your machine as a Mongo service. Not in a disk file like &lt;code&gt;diskdb&lt;/code&gt;. You can manage it from anywhere through the Atlas web app. For your app, you only need a connection URL to access the db.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;😎 &lt;strong&gt;Very cool UI&lt;/strong&gt;: Atlas dashboard is very intuitive, and first-time users will find it easy to use with the visual cues and steps.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  Cloud setup
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;There might be some slight screenshot differences to the actual Atlas UI by the time you do this, but the described steps should get you to the same results.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  1. First sign up here:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.mongodb.com/cloud/atlas/register"&gt;https://www.mongodb.com/cloud/atlas/register&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fV55hflS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1575603471/devto/quick_node_express_mongodb/2019-09-14-15-19-41.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fV55hflS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1575603471/devto/quick_node_express_mongodb/2019-09-14-15-19-41.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Create a free tier &lt;em&gt;Starter Cluster&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fKELuwot--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1575603471/devto/quick_node_express_mongodb/2019-12-01-19-33-18.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fKELuwot--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1575603471/devto/quick_node_express_mongodb/2019-12-01-19-33-18.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The free &lt;strong&gt;Starter Cluster M0&lt;/strong&gt; comes with 512 MB Storage, shared RAM and vCPU.&lt;/p&gt;

&lt;p&gt;This should be more than enough for our starter Node project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Cluster region&lt;/em&gt;: you can pick any provider (AWS, Google, Azure) and the region closest to you that has a Free Tier. For me, it was AWS us-east-1.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FSEPQ8yq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1575603471/devto/quick_node_express_mongodb/2019-09-14-16-15-51.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FSEPQ8yq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1575603471/devto/quick_node_express_mongodb/2019-09-14-16-15-51.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You can leave the rest at FREE defaults.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create the cluster.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;The creation and provisioning might take a few minutes&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Afterwards, the dashboard appears, with the &lt;strong&gt;side panel&lt;/strong&gt; that contains all the options needed to manage clusters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bWwbJ-_h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1575603471/devto/quick_node_express_mongodb/2019-12-01-20-51-36.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bWwbJ-_h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1575603471/devto/quick_node_express_mongodb/2019-12-01-20-51-36.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Connect to the Cluster
&lt;/h3&gt;

&lt;p&gt;Click [&lt;strong&gt;Connect&lt;/strong&gt;] in the Cluster we have just created.&lt;/p&gt;

&lt;p&gt;The dialog that appears gives options to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Whitelist your IP address&lt;/li&gt;
&lt;li&gt;Create a MongoDB user&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZHVbb_lM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1575603471/devto/quick_node_express_mongodb/2019-12-01-20-55-07.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZHVbb_lM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1575603471/devto/quick_node_express_mongodb/2019-12-01-20-55-07.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  3.1 Whitelist IP Address
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Whitelisting&lt;/em&gt; means only allowing access to selected (thus trusted) entities&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;[&lt;strong&gt;Add Your Current IP address&lt;/strong&gt;], which should automatically get your current public IP address. Optionally add a description such as "My Home IP address"&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❗ Remember to do this step again when you switch WiFi, e.g. when moving from coffee shop to home WiFi.&lt;/li&gt;
&lt;li&gt;💡 If you are in a public wi-fi, you can use a service like &lt;a href="https://whatismyipaddress.com/"&gt;https://whatismyipaddress.com/&lt;/a&gt; to get your IPv4 public address, since &lt;code&gt;ipconfig&lt;/code&gt; or &lt;code&gt;ifconfig&lt;/code&gt; might give you only the internal network address.&lt;/li&gt;
&lt;li&gt;💣You can use &lt;code&gt;0.0.0.0/0&lt;/code&gt; to &lt;strong&gt;enable all IP addresses&lt;/strong&gt; anywhere, which simplifies this process, but makes it VERY INSECURE.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  3.2 Create admin user.
&lt;/h3&gt;

&lt;p&gt;Enter your preferred &lt;strong&gt;username&lt;/strong&gt; and &lt;strong&gt;password&lt;/strong&gt;. This will be the first admin ("root") user, then [&lt;strong&gt;Create MongoDB User&lt;/strong&gt;].&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Keep these credentials handy, since we'll use them later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Close the window. We'll connect to the cluster later.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  4. 📃 Add sample data
&lt;/h3&gt;

&lt;p&gt;From the cluster view, click [&lt;strong&gt;Collections&lt;/strong&gt;], then [&lt;strong&gt;Add my Own Data&lt;/strong&gt;]&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--b890OhN1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1575603471/devto/quick_node_express_mongodb/2019-09-15-20-32-17.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--b890OhN1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1575603471/devto/quick_node_express_mongodb/2019-09-15-20-32-17.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Same as the last lesson, we use: 

&lt;ul&gt;
&lt;li&gt;database name: &lt;code&gt;data&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;collection name: &lt;code&gt;movies&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;🎥 The movies from this series is from IMDB. You could use the &lt;code&gt;id&lt;/code&gt; from any movie page URL. E.g. In "The Lion King" page &lt;code&gt;https://www.imdb.com/title/tt0110357&lt;/code&gt;; &lt;code&gt;id&lt;/code&gt; is &lt;code&gt;tt0110357&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Insert Document&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sh6uYqNa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1575603471/devto/quick_node_express_mongodb/2019-09-15-20-35-59.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sh6uYqNa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1575603471/devto/quick_node_express_mongodb/2019-09-15-20-35-59.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Insert a movie entry, as before - Leave the given &lt;code&gt;_id&lt;/code&gt; provided by Atlas since this is internal&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--beC26_ID--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1575603471/devto/quick_node_express_mongodb/2019-09-15-20-37-52.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--beC26_ID--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1575603471/devto/quick_node_express_mongodb/2019-09-15-20-37-52.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Now we see the inserted data in the dashboard. Add more as you wish&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Rppkfwr1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1575603471/devto/quick_node_express_mongodb/2019-09-15-20-39-09.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Rppkfwr1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1575603471/devto/quick_node_express_mongodb/2019-09-15-20-39-09.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Perfect. Now we got data. Time to connect to the cluster&lt;/p&gt;
&lt;h3&gt;
  
  
  5. 🔌 Connect to cluster
&lt;/h3&gt;

&lt;p&gt;Go back to the Clusters view, then click Connect in our cluster.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t4kPfigF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1575603471/devto/quick_node_express_mongodb/2019-09-14-16-34-22.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t4kPfigF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1575603471/devto/quick_node_express_mongodb/2019-09-14-16-34-22.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choose [&lt;strong&gt;Connect your Application&lt;/strong&gt;]&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2qj5osZw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1575603471/devto/quick_node_express_mongodb/2019-09-14-16-34-50.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2qj5osZw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1575603471/devto/quick_node_express_mongodb/2019-09-14-16-34-50.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Leave the default Node.js version 3+&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i6147c2K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1575603471/devto/quick_node_express_mongodb/2019-09-15-20-16-35.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i6147c2K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dvfhgkkpe/image/upload/v1575603471/devto/quick_node_express_mongodb/2019-09-15-20-16-35.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy the &lt;em&gt;Connection String&lt;/em&gt;. We'll use this in the next step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now we're ready to dive into code!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/5zkZRNNb7WW8B9iN3Q/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/5zkZRNNb7WW8B9iN3Q/giphy.gif" alt="Dive"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Connecting to the db
&lt;/h1&gt;
&lt;h3&gt;
  
  
  1. Grab the starter code from last lesson here:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/lenmorld/devto_posts/tree/master/quick_node_express_diskdb"&gt;https://github.com/lenmorld/devto_posts/tree/master/quick_node_express_diskdb&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install &lt;code&gt;mongodb&lt;/code&gt; driver
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;mongodb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Create a new file called &lt;code&gt;db.js&lt;/code&gt; in the app root.
&lt;/h3&gt;

&lt;p&gt;Use this snippet, and replace &lt;code&gt;CONNECTION_STRING_FROM_ATLAS&lt;/code&gt; with your connection string.&lt;/p&gt;

&lt;p&gt;It should look a bit like this&lt;br&gt;
&lt;code&gt;mongodb+srv://myusername:mypassword@cluster0-somelocation.mongodb.net/test?retryWrites=true&amp;amp;w=majority&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// db.js&lt;/span&gt;

&lt;span class="c1"&gt;// mongodb driver&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MongoClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mongodb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;MongoClient&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;dbConnectionUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CONNECTION_STRING_FROM_ATLAS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;dbName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;dbCollectionName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;successCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;failureCallback&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;MongoClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dbConnectionUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dbInstance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[MongoDB connection] ERROR: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;failureCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// this should be "caught" by the calling function&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dbInstance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dbName&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;dbCollection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dbObject&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="nx"&gt;dbCollectionName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[MongoDB connection] SUCCESS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="nx"&gt;successCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dbCollection&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="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="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;initialize&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;The function accepts our cloud db details (db name and collection name) and callbacks.&lt;/li&gt;
&lt;li&gt;It then connects to our cloud db using the driver method &lt;code&gt;MongoClient&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Upon success, it calls the &lt;code&gt;successCallback&lt;/code&gt; passing the &lt;code&gt;dbCollection&lt;/code&gt; object.

&lt;ul&gt;
&lt;li&gt;Any failure will be thrown by &lt;code&gt;failureCallback&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;We'll use &lt;code&gt;dbCollection&lt;/code&gt; to execute mongodb commands.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Back to our server, we'll start &lt;code&gt;server.js&lt;/code&gt; from scratch.
&lt;/h3&gt;

&lt;p&gt;We will be using the cloud db connection instead of &lt;code&gt;diskdb&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// server.js&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&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;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&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;body_parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;body-parser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// parse JSON (application/json content-type)&lt;/span&gt;
&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body_parser&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// &amp;lt;&amp;lt; db setup &amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./db&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;dbName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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;collectionName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;movies&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// &amp;lt;&amp;lt; db init &amp;gt;&amp;gt;&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Server listening at &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;In &lt;code&gt;&amp;lt;&amp;lt; db setup &amp;gt;&amp;gt;&lt;/code&gt;, we import &lt;code&gt;db.js&lt;/code&gt; (to use our &lt;code&gt;initialize&lt;/code&gt; method), and define variables for the db's info.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Initialize the database connection.
&lt;/h3&gt;


&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// db.js&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;// &amp;lt;&amp;lt; db init &amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dbName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;collectionName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dbCollection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// successCallback&lt;/span&gt;
    &lt;span class="c1"&gt;// get all items&lt;/span&gt;
    &lt;span class="nx"&gt;dbCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;toArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// &amp;lt;&amp;lt; db CRUD routes &amp;gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// failureCallback&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;We &lt;code&gt;initialize&lt;/code&gt; the db with the &lt;code&gt;dbName&lt;/code&gt;, &lt;code&gt;collectionName&lt;/code&gt;, and a &lt;code&gt;successCallback&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Inside the &lt;code&gt;successCallback&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;GET all&lt;/code&gt; using &lt;code&gt;collection.find()&lt;/code&gt;, which returns a &lt;em&gt;cursor&lt;/em&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;em&gt;cursor&lt;/em&gt; is like an iterator, where you can do &lt;code&gt;next&lt;/code&gt;, &lt;code&gt;hasNext()&lt;/code&gt;, etc&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Convert the cursor to an array using &lt;strong&gt;async&lt;/strong&gt; method &lt;code&gt;toArray(callback)&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's simpler for our use case to return full array, than iterate the cursor.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;callback&lt;/code&gt; runs after successfully converting the cursor to an array&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We log the result for now, but you can imagine the code for returning response to client goes here&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Start server
&lt;/h4&gt;


&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;npm start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;...which should give:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;Server listening at 4000
&lt;span class="o"&gt;[&lt;/span&gt;MongoDB connection] SUCCESS
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; _id: 5d7ed8f31c9d4400009c3775,
    &lt;span class="nb"&gt;id&lt;/span&gt;: &lt;span class="s1"&gt;'tt0110357'&lt;/span&gt;,
    name: &lt;span class="s1"&gt;'The Lion King'&lt;/span&gt;,
    genre: &lt;span class="s1"&gt;'animation'&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Yay! It works!
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/hQiyolNu6eF2xvq6zH/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/hQiyolNu6eF2xvq6zH/giphy.gif" alt="works"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now let's complete all the CRUD routes!&lt;/strong&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  CRUD routes
&lt;/h1&gt;

&lt;p&gt;Here's a rundown of the CRUD-to-MongoDB operations for our route handlers.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Notice that there are quite some syntax differences between &lt;code&gt;diskdb&lt;/code&gt; functions and the official &lt;code&gt;MongoClient&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CRUD Operation&lt;/th&gt;
&lt;th&gt;REST operation&lt;/th&gt;
&lt;th&gt;MongoClient Operation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Create&lt;/td&gt;
&lt;td&gt;POST &lt;code&gt;/items&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dbCollection.insertOne(object, callback)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read One&lt;/td&gt;
&lt;td&gt;GET &lt;code&gt;/items/:id&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dbCollection.findOne(query callback)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read All&lt;/td&gt;
&lt;td&gt;GET &lt;code&gt;/items&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dbCollection.find(query).toArray(callback)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Update&lt;/td&gt;
&lt;td&gt;PUT &lt;code&gt;/items/:id&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dbCollection.updateOne(query, { $set: obj }, callback)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete&lt;/td&gt;
&lt;td&gt;DELETE &lt;code&gt;/items/:id&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dbCollection.deleteOne(query, callback)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All of these routes go in the &lt;code&gt;&amp;lt;&amp;lt; db CRUD routes &amp;gt;&amp;gt;&lt;/code&gt; marker in our code.&lt;/p&gt;
&lt;h3&gt;
  
  
  i. Create ➕
&lt;/h3&gt;


&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// server.js&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;// &amp;lt;&amp;lt; db CRUD routes &amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/items&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;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;dbCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;insertOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// callback of insertOne&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// return updated list&lt;/span&gt;
        &lt;span class="nx"&gt;dbCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;toArray&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;_error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// callback of find&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;_error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="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="nx"&gt;_result&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;ol&gt;
&lt;li&gt;&lt;p&gt;For the &lt;code&gt;POST /items&lt;/code&gt; handler, use &lt;code&gt;insertOne(item, callback)&lt;/code&gt; to add the movie from &lt;code&gt;request.body&lt;/code&gt; (parsed by &lt;code&gt;body_parser&lt;/code&gt; middleware)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the &lt;code&gt;callback&lt;/code&gt; of &lt;code&gt;insertOne&lt;/code&gt;, throw the &lt;code&gt;error&lt;/code&gt; if any. The &lt;code&gt;result&lt;/code&gt; is not used here (_which is just a boolean for success and &lt;code&gt;_id&lt;/code&gt; of inserted document).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Get the updated list using &lt;code&gt;find()&lt;/code&gt;, and return the &lt;code&gt;_result&lt;/code&gt; as the response in its &lt;code&gt;callback&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Note the two levels of similar callbacks here: outer callback of &lt;code&gt;insertOne&lt;/code&gt;, and inner one of &lt;code&gt;find&lt;/code&gt;. This is why I used &lt;code&gt;(_error, _result)&lt;/code&gt; in the inner to avoid name collision. But feel free to rename them 😉&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Test:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{"id": "tt0109830", "name": "Forrest
Gump", "genre": "drama"}'&lt;/span&gt; http://localhost:4000/items

&lt;span class="o"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;"_id"&lt;/span&gt;:&lt;span class="s2"&gt;"5de5c9d01c9d440000482ef0"&lt;/span&gt;,&lt;span class="s2"&gt;"id"&lt;/span&gt;:&lt;span class="s2"&gt;"tt0110357"&lt;/span&gt;,&lt;span class="s2"&gt;"name"&lt;/span&gt;:&lt;span class="s2"&gt;"The Lion King"&lt;/span&gt;,&lt;span class="s2"&gt;"genre"&lt;/span&gt;:&lt;span class="s2"&gt;"animation"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"_id"&lt;/span&gt;:&lt;span class="s2"&gt;"5de7009967aec74a90f88d67"&lt;/span&gt;,&lt;span class="s2"&gt;"id"&lt;/span&gt;:&lt;span class="s2"&gt;"tt0109830"&lt;/span&gt;,&lt;span class="s2"&gt;"name"&lt;/span&gt;:&lt;span class="s2"&gt;"Forrest Gump"&lt;/span&gt;,&lt;span class="s2"&gt;"genre"&lt;/span&gt;:&lt;span class="s2"&gt;"drama"&lt;/span&gt;&lt;span class="o"&gt;}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  ii. Read one 🕵️
&lt;/h3&gt;


&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/items/:id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;itemId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;dbCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;itemId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// return item&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="nx"&gt;result&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;ol&gt;
&lt;li&gt;&lt;p&gt;Get the &lt;code&gt;id&lt;/code&gt; directly from the params (e.g. &lt;code&gt;1234&lt;/code&gt; for &lt;a href="http://localhost/items/1234"&gt;http://localhost/items/1234&lt;/a&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Find the item with that &lt;code&gt;id&lt;/code&gt; using &lt;code&gt;findOne(query)&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;query&lt;/code&gt; is just an object so you can use key-value pairs for your queries. We use this query object for &lt;code&gt;find&lt;/code&gt;, &lt;code&gt;delete&lt;/code&gt; and other MongoDB commands.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// query can be:&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// find using id&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The Lion King&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// find using name&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The Lion King&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;genre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;action&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// find using id, name and genre&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Return the item in the &lt;code&gt;response&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Test:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://localhost:4000/items/tt0109830

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"_id"&lt;/span&gt;:&lt;span class="s2"&gt;"5de7009967aec74a90f88d67"&lt;/span&gt;,&lt;span class="s2"&gt;"id"&lt;/span&gt;:&lt;span class="s2"&gt;"tt0109830"&lt;/span&gt;,&lt;span class="s2"&gt;"name"&lt;/span&gt;:&lt;span class="s2"&gt;"Forrest Gump"&lt;/span&gt;,&lt;span class="s2"&gt;"genre"&lt;/span&gt;:&lt;span class="s2"&gt;"drama"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  iii. Read all 🕵️
&lt;/h3&gt;


&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/items&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;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// return updated list&lt;/span&gt;
    &lt;span class="nx"&gt;dbCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;toArray&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Return all the items in the collection in the response, same in &lt;strong&gt;POST /items&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Test:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://localhost:4000/items

&lt;span class="o"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;"_id"&lt;/span&gt;:&lt;span class="s2"&gt;"5de5c9d01c9d440000482ef0"&lt;/span&gt;,&lt;span class="s2"&gt;"id"&lt;/span&gt;:&lt;span class="s2"&gt;"tt0110357"&lt;/span&gt;,&lt;span class="s2"&gt;"name"&lt;/span&gt;:&lt;span class="s2"&gt;"The Lion King"&lt;/span&gt;,&lt;span class="s2"&gt;"genre"&lt;/span&gt;:&lt;span class="s2"&gt;"animation"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"_id"&lt;/span&gt;:&lt;span class="s2"&gt;"5de7009967aec74a90f88d67"&lt;/span&gt;,&lt;span class="s2"&gt;"id"&lt;/span&gt;:&lt;span class="s2"&gt;"tt0109830"&lt;/span&gt;,&lt;span class="s2"&gt;"name"&lt;/span&gt;:&lt;span class="s2"&gt;"Forrest Gump"&lt;/span&gt;,&lt;span class="s2"&gt;"genre"&lt;/span&gt;:&lt;span class="s2"&gt;"drama"&lt;/span&gt;&lt;span class="o"&gt;}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  iv. Update ✏️
&lt;/h3&gt;


&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/items/:id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;itemId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Editing item: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;itemId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; to be &lt;/span&gt;&lt;span class="dl"&gt;"&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;dbCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;updateOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;itemId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;$set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// send back entire updated list, to make sure frontend data is up-to-date&lt;/span&gt;
        &lt;span class="nx"&gt;dbCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;toArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;_error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="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="nx"&gt;_result&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;ol&gt;
&lt;li&gt;&lt;p&gt;Get the &lt;code&gt;id&lt;/code&gt; from params and the &lt;code&gt;item&lt;/code&gt; from body (through &lt;code&gt;body-parser&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Update item with &lt;code&gt;id&lt;/code&gt; and set it to &lt;code&gt;item&lt;/code&gt;, using &lt;code&gt;dbCollection.updateOne(query, { $set: item }, callback&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Note the use of MongoDB-specific &lt;code&gt;{ $set: item }&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Return the updated list, as in &lt;code&gt;POST /items&lt;/code&gt; and &lt;code&gt;GET /items&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Test:&lt;/p&gt;

&lt;p&gt;Maybe you think "The Lion King" is a drama, since ...well, I won't spoil it. 🤫 🦁&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; PUT &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{"genre": "drama"}'&lt;/span&gt; http://localhost:4000/items/tt0110357

&lt;span class="o"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;"_id"&lt;/span&gt;:&lt;span class="s2"&gt;"5de5c9d01c9d440000482ef0"&lt;/span&gt;,&lt;span class="s2"&gt;"id"&lt;/span&gt;:&lt;span class="s2"&gt;"tt0110357"&lt;/span&gt;,&lt;span class="s2"&gt;"name"&lt;/span&gt;:&lt;span class="s2"&gt;"The Lion King"&lt;/span&gt;,&lt;span class="s2"&gt;"genre"&lt;/span&gt;:&lt;span class="s2"&gt;"drama"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"_id"&lt;/span&gt;:&lt;span class="s2"&gt;"5de7009967aec74a90f88d67"&lt;/span&gt;,&lt;span class="s2"&gt;"id"&lt;/span&gt;:&lt;span class="s2"&gt;"tt0109830"&lt;/span&gt;,&lt;span class="s2"&gt;"name"&lt;/span&gt;:&lt;span class="s2"&gt;"Forrest Gump"&lt;/span&gt;,&lt;span class="s2"&gt;"genre"&lt;/span&gt;:&lt;span class="s2"&gt;"drama"&lt;/span&gt;&lt;span class="o"&gt;}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  v. Delete ❌
&lt;/h3&gt;


&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/items/:id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;itemId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Delete item with id: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;itemId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;dbCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deleteOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;itemId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// send back entire updated list after successful request&lt;/span&gt;
        &lt;span class="nx"&gt;dbCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;toArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;_error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="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="nx"&gt;_result&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;Here, only the &lt;code&gt;id&lt;/code&gt; is needed from params, which we pass to &lt;code&gt;dbCollection.deleteOne(query)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As before, you can formulate a query easily to your needs, since it's just an object.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🤸‍♀️ Challenge: modularize the &lt;code&gt;dbCollection.find()&lt;/code&gt; since we're using it in 3 places.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Test:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; DELETE http://localhost:4000/items/tt0109830

&lt;span class="o"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;"_id"&lt;/span&gt;:&lt;span class="s2"&gt;"5de5c9d01c9d440000482ef0"&lt;/span&gt;,&lt;span class="s2"&gt;"id"&lt;/span&gt;:&lt;span class="s2"&gt;"tt0110357"&lt;/span&gt;,&lt;span class="s2"&gt;"name"&lt;/span&gt;:&lt;span class="s2"&gt;"The Lion King"&lt;/span&gt;,&lt;span class="s2"&gt;"genre"&lt;/span&gt;:&lt;span class="s2"&gt;"drama"&lt;/span&gt;&lt;span class="o"&gt;}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Notes
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Callbacks ??!!&lt;/strong&gt;, why this instead of ES6 Promises, or ES7 async/await...&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MongoClient.connect&lt;/code&gt; only supports callbacks, but we'll &lt;em&gt;promisify&lt;/em&gt; (and &lt;em&gt;aysnc-await-ify&lt;/em&gt;) these callbacks on the next lesson, since honestly they are starting to look like &lt;strong&gt;callback hell&lt;/strong&gt;. See this post for a fun rundown on &lt;strong&gt;callback hell&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/amberjones" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4LnlbCHm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--GJQx3267--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/238843/52bb809e-e068-4771-a53e-ad3f2548fc24.jpg" alt="amberjones image"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/amberjones/how-to-escape-callback-hell-with-javascipt-promises-42d0" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;How to Escape Callback Hell with JavaScipt Promises&lt;/h2&gt;
      &lt;h3&gt;AmberJ ・ Oct 21 '19 ・ 4 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#beginners&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Why do We return all of the items in the response &lt;strong&gt;create, update, delete&lt;/strong&gt; ?&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;There are a lot of options on what to do to synchronize UI and backend after a change, and it is quite a &lt;em&gt;Software Architecture&lt;/em&gt; topic for itself.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag__link"&gt;
  &lt;div class="ltag__link__content"&gt;
    &lt;div class="missing"&gt;
      &lt;h2&gt;Article No Longer Available&lt;/h2&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Here, we just return the updated items to UI after a create, update and delete. We let the frontend (e.g. React, Vue, Angular, Vanilla JS) update its state and views from that information.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Here's the complete Code
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/lenmorld/devto_posts/tree/master/quick_node_express_mongodb"&gt;https://github.com/lenmorld/devto_posts/tree/master/quick_node_express_mongodb&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/gGuOldphm6vzW/source.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/gGuOldphm6vzW/source.gif" alt="hackerman"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;em&gt;"Okay, that was nice. But what can I do with this? "&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;✅ Serving HTML files&lt;br&gt;
✅ REST API&lt;br&gt;
✅ Cloud Database persistence&lt;/p&gt;

&lt;p&gt;Now, this is an actual server for a small project. Add more routes, save some data in the db through API requests. Just add frontend!™&lt;/p&gt;
&lt;h2&gt;
  
  
  Next up:
&lt;/h2&gt;

&lt;p&gt;(In progress)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MongoDB callbacks to Promises and Async/Await&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;This article is part of a Node+Express series I'm working on.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For the meantime, if you can't get enough of Node+Express 🤓,&lt;br&gt;
checkout my Node workshop (Gihub repo and slides):&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/lenmorld"&gt;
        lenmorld
      &lt;/a&gt; / &lt;a href="https://github.com/lenmorld/node_workshop"&gt;
        node_workshop
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Build a server and API for your next web application, using Node, Express and MongoDB
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
Node workshop&lt;/h1&gt;
&lt;h3&gt;
to follow workshop:&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ git checkout dev
$ node server.js
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;
to dev latest&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ git checkout master
$ npm start
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;
Create a .env file on root&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;NODE_ENV=development
PORT=4000
MONGO_DB_CONNECTION_STRING=YOUR_CONNECTION_STRING_HERE
# API keys go here
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/lenmorld/node_workshop"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;&lt;br&gt;
Here we discussed:

&lt;ul&gt;
&lt;li&gt;Using Node and Express&lt;/li&gt;
&lt;li&gt;Routing, request and response&lt;/li&gt;
&lt;li&gt;Building a REST API&lt;/li&gt;
&lt;li&gt;Server-rendered templates&lt;/li&gt;
&lt;li&gt;Connecting to a NoSQL (mongo) database&lt;/li&gt;
&lt;li&gt;Using external APIs, such as Spotify&lt;/li&gt;
&lt;li&gt;and much more!&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Happy server-ing! 🤓&lt;/p&gt;




&lt;p&gt;Now, all this Mongo talk made me hungry.&lt;br&gt;
I'm gonna have some of my favorite "Hopia Mongo" (bean-filled Filipino pastry)🌱.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--U6QFc6Gq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.foxyfolksy.com/wp-content/uploads/2016/03/hopia-recipe-1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--U6QFc6Gq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.foxyfolksy.com/wp-content/uploads/2016/03/hopia-recipe-1.jpg" alt="Hopia Mongo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Photo from FoxyFolksy in article "Hopia Recipe- Munggo And Ube Filling" src: &lt;a href="https://www.foxyfolksy.com/hopia-recipe-munggo-ube-filling/"&gt;https://www.foxyfolksy.com/hopia-recipe-munggo-ube-filling/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>express</category>
      <category>database</category>
      <category>mongodb</category>
    </item>
    <item>
      <title>How much time on average do you spent coding outside of full-time work?</title>
      <dc:creator>Lenmor Ld</dc:creator>
      <pubDate>Thu, 16 May 2019 22:15:34 +0000</pubDate>
      <link>https://forem.com/lennythedev/how-much-time-on-average-do-you-spent-coding-outside-of-full-time-work-52g</link>
      <guid>https://forem.com/lennythedev/how-much-time-on-average-do-you-spent-coding-outside-of-full-time-work-52g</guid>
      <description>&lt;p&gt;If you're like me, I have an imaginary backlog full of ideas I want to test out, tech I want to learn next, articles and tutorials I want to explore, and projects I want to work on. &lt;/p&gt;

&lt;p&gt;But between 40 hours a week at work, going to gym and trying to have enough time to rest, I'm finding it a challenge to hit the right amount of hours in a day / week that's a good amount.&lt;/p&gt;

&lt;p&gt;I want to get an idea on how many hours devs spent on average, and no better place to ask than Dev.to 👨‍💻👩‍💻&lt;/p&gt;

&lt;p&gt;I'll start:&lt;/p&gt;

&lt;h3&gt;
  
  
  Me: 16 hours a week on average, with varying focus and regularity
&lt;/h3&gt;

&lt;p&gt;weeknights: 3 hrs per day, except Friday (rest day)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;less focus: most of energy consumed during the day&lt;/li&gt;
&lt;li&gt;regular: it's easier to get it going 4 straight days in a row&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;weekends: 2 hours per day&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;better focus: full brain power, since no work&lt;/li&gt;
&lt;li&gt;irregular: because of errands, cooking, going out, friends, family. It's the weekend after all 😄 🛍️ 🏞️ 🚴‍♂️ 🏀&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What about you?
&lt;/h2&gt;

&lt;p&gt;(Details optional, main thing is the hrs per day/week)&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>life</category>
      <category>wellness</category>
      <category>coding</category>
    </item>
    <item>
      <title>What is the standard way to keep UI state and backend state synced during updates? (React and Node)</title>
      <dc:creator>Lenmor Ld</dc:creator>
      <pubDate>Thu, 09 May 2019 22:07:57 +0000</pubDate>
      <link>https://forem.com/lennythedev/what-is-the-standard-way-to-keep-ui-state-and-backend-state-synced-during-updates-react-and-node-plm</link>
      <guid>https://forem.com/lennythedev/what-is-the-standard-way-to-keep-ui-state-and-backend-state-synced-during-updates-react-and-node-plm</guid>
      <description>&lt;p&gt;Let's say i have a Node-Express backend with a REST API and a React frontend that has some editable Card. On App load, I fetch an &lt;em&gt;items array&lt;/em&gt; and do a setState&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;componentDidMount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/items&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;  &lt;span class="c1"&gt;// res.data is array of items&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;When user updates the card, I send a PUT or POST request to the backend, &lt;br&gt;
which updates the card details (with or without DB doesnt matter, just assume backend state is updated). good.&lt;/p&gt;

&lt;p&gt;My question is, what's the best way to update the UI ?&lt;br&gt;
Is there a standard way when doing REST?&lt;/p&gt;

&lt;p&gt;Here are the ideas I could come up with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;On my server PUT/POST route handler, i could return &lt;em&gt;items array&lt;/em&gt;, including the one updated. On UI, I could then do a simple &lt;code&gt;setState({ items: res.data })&lt;/code&gt;. Returning the entire items array seems expensive though, considering only one item is updated.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At least backend is source of truth, if update fails, i could easily tell UI error happened.&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;On my server Put/POST route handler, i could return &lt;em&gt;only updated item&lt;/em&gt;. &lt;br&gt;
I would then have to find and replace the updated item in my state and  do a &lt;code&gt;setState({ items: res.data })&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple enough, but it feels like i'm doing extra work.
But since my data structure is an array, I have to replace entire array in UI state. &lt;/li&gt;
&lt;li&gt;Or is there a better way to update only one item in an array in state, or maybe better to change my data structure to an object, so setState is optimal.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On my server Put/POST route handler, i could return a &lt;em&gt;status code/message&lt;/em&gt; to signal UI that operation succeeded and now I can update UI state.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On UI side, I do the update first, then send the updates to backend.&lt;br&gt;&lt;br&gt;
For some reason if it fails in backend, then I rollback UI changes (might be tricky?)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use localStorage in between?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;any other ideas?&lt;/p&gt;

&lt;p&gt;This is probably handled better in GraphQL and using state management like Flux, &lt;br&gt;
but if i'm doing it really vanilla Node and React, what's the best way?&lt;/p&gt;

&lt;p&gt;Let me know if my question isn't clear enough, i could elaborate&lt;/p&gt;

&lt;p&gt;Thanks!&lt;/p&gt;

</description>
      <category>help</category>
      <category>react</category>
      <category>node</category>
      <category>discuss</category>
    </item>
  </channel>
</rss>
