<?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: Andrew Evans</title>
    <description>The latest articles on Forem by Andrew Evans (@andrewevans0102).</description>
    <link>https://forem.com/andrewevans0102</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%2F178117%2Fe8213fca-5867-49c7-8cbb-08efa4b22d52.jpeg</url>
      <title>Forem: Andrew Evans</title>
      <link>https://forem.com/andrewevans0102</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/andrewevans0102"/>
    <language>en</language>
    <item>
      <title>Building Microfrontends with Firebase</title>
      <dc:creator>Andrew Evans</dc:creator>
      <pubDate>Tue, 19 Nov 2024 12:00:50 +0000</pubDate>
      <link>https://forem.com/andrewevans0102/building-microfrontends-with-firebase-25bn</link>
      <guid>https://forem.com/andrewevans0102/building-microfrontends-with-firebase-25bn</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The post you are reading is cross posted from my blog site. Please see my original post at &lt;a href="https://andrewevans.dev/blog/2024-11-19-building-microfrontends-with-firebase/" rel="noopener noreferrer"&gt;andrewevans.dev&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Recently I've been working with Microfrontends and wanted to put together a post that covers some basic concepts and includes an example project. I'm also a Firebase fan and wanted to include how you could build a Microfrontend with Firebase. I'm using React in this project, but if you wanted to pick a different Frontend Library or Framework the same basic principles should apply. Overall this post will cover what a Microfrontend is, and showcase an example project that shows you a working project. If you'd like to follow along or just jump straight into the code, checkout my GitHub repo &lt;a href="https://github.com/andrewevans0102/firebase-mfe" rel="noopener noreferrer"&gt;firebase-mfe&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Microfrontend?
&lt;/h2&gt;

&lt;p&gt;Microfrontends (also called MFEs for short) have been around for several years now, but the core principles and pattern have remained the same. MFEs are a way to break down a large application into smaller pieces that can be deployed independently. The business value of MFEs, is the ability to break a large application into smaller teams that can be developed and released faster.&lt;/p&gt;

&lt;p&gt;Consider as an example a web application that is maintained by 50 developers. Consider this application to actually consist of multiple pages that are independent parts (things like a shopping cart page, an info page, etc.). Anytime developers make changes, this could impact any and all parts of the same shared application. This is typically called a &lt;code&gt;monolith&lt;/code&gt; whereby everything operates in the same codebase and any change impacts the entire application.&lt;/p&gt;

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

&lt;p&gt;Conversely, consider taking that same application with the 50 developers and multiple pages and breaking it into pieces. Imagine having specific teams focus on specific parts of the application without worrying about potentially breaking or stopping another teams progress. The example of 50 developers could conversely be considered as a project with 5 teams of 10 developers. Each team focusing on a specific area (or page) of the Frontend application. This is the real value of this pattern in that it can stop many of the pain points of managing large systems.&lt;/p&gt;

&lt;p&gt;To visually show this, one also has to point out that MFEs work with a &lt;code&gt;host&lt;/code&gt; and &lt;code&gt;remotes&lt;/code&gt;. The &lt;code&gt;host&lt;/code&gt; manages the application as a while and consumes &lt;code&gt;remote&lt;/code&gt; applications. The &lt;code&gt;remotes&lt;/code&gt; expose components that are then consumed by the host. Modifying the earlier visualization to be shown in terms of MFEs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftq44i60gst4vuf3n5lyo.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftq44i60gst4vuf3n5lyo.jpg" alt="MFE Host and Remote Visual" width="800" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each of the &lt;code&gt;remotes&lt;/code&gt; should be able to be independently developed and deployed. The &lt;code&gt;host&lt;/code&gt; can also manage things like tokens or shared settings that can be consumed by the remotes. The &lt;code&gt;remotes&lt;/code&gt; can take in values from the &lt;code&gt;host&lt;/code&gt; as props within the components. This is really powerful because it means that in a corporate environment, a team could manage things like app settings and authentication and then the remote teams could all use the same values. This means each team doesn't have to go through the process of setting up their own resources.&lt;/p&gt;

&lt;p&gt;At its core, MFEs also take advantage of &lt;a href="https://webpack.js.org/concepts/module-federation/" rel="noopener noreferrer"&gt;Module Federation&lt;/a&gt; and Webpack. Module Federation has the following key concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sharing components (or modules) between pieces of an application through exposing or consuming (versus building everything at once)&lt;/li&gt;
&lt;li&gt;Sharing dependencies within an application (not having to copy the same code in multiple areas)&lt;/li&gt;
&lt;li&gt;On demand loading of dependencies (only load what you need)&lt;/li&gt;
&lt;li&gt;Deploying and developing parts of an application independently&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With Module Federation the &lt;code&gt;host&lt;/code&gt; will &lt;code&gt;consume&lt;/code&gt; remotes and the remotes will &lt;code&gt;expose&lt;/code&gt; components to be &lt;code&gt;consumed&lt;/code&gt; by the &lt;code&gt;host&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you do some Googling on this topic, you'll find that many people have different takes on the way that this is implemented. There are a variety of ways to manage an architecture like this. The overarching concepts are still the same. In the next sections, I'll share my example application &lt;a href="https://github.com/andrewevans0102/firebase-mfe" rel="noopener noreferrer"&gt;firebase-mfe&lt;/a&gt;. and show how it accomplishes the MFE Architecture with Firebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building an MFE with Firebase
&lt;/h2&gt;

&lt;p&gt;Before you begin, you need to go to the &lt;a href="https://console.firebase.google.com/" rel="noopener noreferrer"&gt;Firebase console&lt;/a&gt; and create an application. If you're new to Firebase, I recommend checking out &lt;a href="https://firebase.google.com/docs/guides" rel="noopener noreferrer"&gt;their fundamentals page&lt;/a&gt;. Firebase for the most part is free to initially get setup. Certain services have costs over time, but for the purposes of this project you shouldn't incur any cost.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffrqse3u9tp0x7f0o7lv7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffrqse3u9tp0x7f0o7lv7.jpg" alt="Firebase register app page" width="800" height="584"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you have the project setup, make sure to go and add an application which will result in configuration values like these:&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;firebaseConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Add your Firebase config here&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;API_KEY&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;authDomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;AUTH_DOMAIN&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;PROJECT_ID&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;storageBucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;STORAGE_BUCKET&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;messagingSenderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;MESSAGING_SENDER_ID&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;APP_ID&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these values setup, you should also go over to the Authentication Service and add a test user. My application uses an Auth value that is passed into the remotes, so we need a test user to be able to validate everything.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgjbn7ijjeq32yrbzohuc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgjbn7ijjeq32yrbzohuc.jpg" alt="Firebase setup authentication page" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With all of this setup, you can now move over to the application to get setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the Host
&lt;/h2&gt;

&lt;p&gt;For my sample application I used React, but you could just as easily use a different library or framework of your choice. The most important part is just that it has to be able to use Webpack so you can make use of Module Federation.&lt;/p&gt;

&lt;p&gt;Since I have a full working project to share, I'm just going to highlight the places that need to be configured for everything to work. The general setup of this sample project could be expanded to a much larger project if one wanted to. One other point is that my sample project is all in the same repository, but you could potentially have your projects in separate repos entirely if you want. One of the best parts about MFEs is that you have the flexibility to customize the setup based on a variety of project needs.&lt;/p&gt;

&lt;p&gt;In my sample application I have three projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;host&lt;/li&gt;
&lt;li&gt;remote1&lt;/li&gt;
&lt;li&gt;remote2&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;host&lt;/code&gt; is where I store the Firebase configuration and you can see this in &lt;code&gt;host/src/firebase.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initializeApp&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;firebase/app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getAuth&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;firebase/auth&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;firebaseConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Add your Firebase config here&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;API_KEY&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;authDomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;AUTH_DOMAIN&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;PROJECT_ID&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;storageBucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;STORAGE_BUCKET&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;messagingSenderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;MESSAGING_SENDER_ID&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;APP_ID&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;initializeApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firebaseConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getAuth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, you'll notice in the sample application I have a &lt;code&gt;bootstrap.tsx&lt;/code&gt; file next to my &lt;code&gt;index.tsx&lt;/code&gt; file. This file is read in by &lt;code&gt;index.tsx&lt;/code&gt; to render my application. The remotes will also have a &lt;code&gt;bootstrap.tsx&lt;/code&gt; file, but I will cover that after I finish this portion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/bootstrap.tsx&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createRoot&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-dom/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&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;./App&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;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;createRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&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;App&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if you go over to the &lt;code&gt;App.tsx&lt;/code&gt; file, you'll note that you have 2 imports at the top that include 2 remotes:&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;Remote1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lazy&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remote1/App&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;Remote2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lazy&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remote2/App&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll also notice that the remotes are defined in the "view" of the &lt;code&gt;App.tsx&lt;/code&gt; component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ErrorBoundary&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;authState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LoginForm&lt;/span&gt; &lt;span class="na"&gt;onLogin&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleLogin&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Welcome &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;authState&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;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt; &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Loading Remote 1..."&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Remote1&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;authState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt; &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Loading Remote 2..."&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Remote2&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;authState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;signOut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Logout&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ErrorBoundary&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also note that the &lt;code&gt;user&lt;/code&gt; object is passed in as a prop to both remotes. We will need to account for that in the next section where I explain the &lt;code&gt;remote1&lt;/code&gt; and &lt;code&gt;remote2&lt;/code&gt; projects.&lt;/p&gt;

&lt;p&gt;If we jump over to the &lt;code&gt;host/config/webpack.config.js&lt;/code&gt; file you will note at the bottom how this is setup with the Module Federation plugin:&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;config&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="nf"&gt;push&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;ModuleFederationPlugin&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;host&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;remotes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;remote1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isEnvDevelopment&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remote1@http://localhost:3001/remoteEntry.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remote1@https://remote1-11182024.firebaseapp.com/remoteEntry.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;remote2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isEnvDevelopment&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remote2@http://localhost:3002/remoteEntry.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remote2@https://remote2-11182024.firebaseapp.com/remoteEntry.js&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;shared&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;react&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;requiredVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../package.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;react&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;eager&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="c1"&gt;// Add this line&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-dom&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;singleton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;requiredVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../package.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-dom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;eager&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="c1"&gt;// Add this line&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;firebase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;requiredVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../package.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firebase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;eager&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="c1"&gt;// Add this line&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;Lets note a few things here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the &lt;code&gt;remotes&lt;/code&gt; block has a definition for remote projects with the prefix that is the name &lt;code&gt;remote1&lt;/code&gt; and &lt;code&gt;remote2&lt;/code&gt;. You'll note that this is the same as the imports in the &lt;code&gt;App.tsx&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Both of the remotes are defined based on if the &lt;code&gt;isEnvDevelopment&lt;/code&gt; flag is set. If in development, load from localhost otherwise point to the deployed sites.&lt;/li&gt;
&lt;li&gt;Both of the &lt;code&gt;remotes&lt;/code&gt; make note of a &lt;code&gt;remoteEntry.js&lt;/code&gt; file, this is what is compiled and read in by the host.&lt;/li&gt;
&lt;li&gt;Note also that in the &lt;code&gt;shared&lt;/code&gt; section it specifies versions of &lt;code&gt;react&lt;/code&gt;, &lt;code&gt;react-dom&lt;/code&gt;, and &lt;code&gt;firebase&lt;/code&gt; based on what is in the remotes.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Please note that the React apps shown here have been "ejected" as part of a &lt;code&gt;create-react-app&lt;/code&gt; project. There are many ways one could have done this, but this was the easiest way I found to get setup with a sample project like this.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With the above setups, you'll also need to run &lt;code&gt;firebase init&lt;/code&gt; and go through the terminal to get the project setup as a "firebase" project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn027flkv793zf24qk44o.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn027flkv793zf24qk44o.jpg" alt="Firebase init terminal screen" width="800" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll then have to either go into the Firebase console, or run commands to generate "sites" within your project that you can deploy to. Here are the commands I ran:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;firebase hosting:sites:create host-11182024
firebase target:apply hosting host-11182024 &amp;lt;PROJECT_ID&amp;gt;-host-11182024
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note that i named my host &lt;code&gt;host-11182024&lt;/code&gt; just as a value I could remember. You could name your host (and remotes) anything you want.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The names I made for the host project were arbitrary. I also had to run these commands a few times before I figured out the correct setting here. Ultimately I ended up with a &lt;code&gt;firebase.json&lt;/code&gt; file that looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hosting"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"host-11182024"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"public"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ignore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"firebase.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"**/.*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"**/node_modules/**"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"rewrites"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"**"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/index.html"&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;I also ended up with a &lt;code&gt;.firebaserc&lt;/code&gt; file that looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"projects"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;YOUR_FIREBASE_PROJECT_ID&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"targets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"&amp;lt;YOUR_FIREBASE_PROJECT_ID&amp;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="nl"&gt;"hosting"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"host-11182024"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"host-11182024"&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="nl"&gt;"etags"&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;Ultimately, this is all just setting it up so when you deploy you are sending the bundle to a specific place in your project. With my sample project I used &lt;code&gt;host-11182024&lt;/code&gt; which ended up me using the deploy command &lt;code&gt;firebase deploy --only hosting:host-11182024&lt;/code&gt;. We will next do similar setups for the Remote projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the Remotes
&lt;/h2&gt;

&lt;p&gt;The remote projects in the sample project are in the &lt;code&gt;remote1&lt;/code&gt; and &lt;code&gt;remote2&lt;/code&gt; folders. I'll just go through &lt;code&gt;remote1&lt;/code&gt; and you can follow the same process for &lt;code&gt;remote2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In remote1, look first at the &lt;code&gt;bootstrap.tsx&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createRoot&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-dom/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&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;./App&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;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Don't render anything in standalone mode for production&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;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;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;development&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./devBootstrap&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;createRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&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;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;This&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;microfrontend&lt;/span&gt; &lt;span class="nx"&gt;that&lt;/span&gt; &lt;span class="nx"&gt;needs&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;be&lt;/span&gt; &lt;span class="nx"&gt;loaded&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;host&lt;/span&gt; &lt;span class="nx"&gt;application&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&amp;gt;&lt;/span&gt;&lt;span class="err"&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;Note that we are accounting for development mode. The &lt;code&gt;devBootstrap.tsx&lt;/code&gt; file includes a mock user. This is because we are passing in the &lt;code&gt;user&lt;/code&gt; object into the remote from the host. Having a mock value allows you to run the remote independently of the host for debugging etc. This is a great example of how you could pass things like tokens or other values from &lt;code&gt;host&lt;/code&gt; to &lt;code&gt;remote&lt;/code&gt;. Just for reference and to continue with the explanation this is what the &lt;code&gt;devBootstrap.tsx&lt;/code&gt; file looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="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="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createRoot&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-dom/client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;firebase/auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test-uid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;emailVerified&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;isAnonymous&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="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="na"&gt;providerData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="na"&gt;refreshToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;tenantId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;delete&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="na"&gt;getIdToken&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="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;getIdTokenResult&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="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;authTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;issuedAtTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;expirationTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;signInProvider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;claims&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
        &lt;span class="na"&gt;signInSecondFactor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;reload&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="na"&gt;toJSON&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="na"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;phoneNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123-123-1234&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;photoURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;providerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;createRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&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;App&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mockUser&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;)
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within the &lt;code&gt;remote1&lt;/code&gt; project we can next take a look at the &lt;code&gt;host/config/webpack.config.js&lt;/code&gt; file where we define the Module Federation plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ModuleFederationPlugin&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;remote1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remoteEntry.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;exposes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./App&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./src/App&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;shared&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;react&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;requiredVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../package.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;.&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-dom&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;singleton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;requiredVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../package.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-dom&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;firebase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;requiredVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../package.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firebase&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;Note that the name &lt;code&gt;remote1&lt;/code&gt; lines up with the name of the value in the &lt;code&gt;host&lt;/code&gt; webpack config. Also note the &lt;code&gt;exposes&lt;/code&gt; field points to the projects &lt;code&gt;App.tsx&lt;/code&gt; file where the main application is loaded. The additional definitions in the &lt;code&gt;shared&lt;/code&gt; section are similar to how we defined the values in the same place in the &lt;code&gt;host&lt;/code&gt; config file.&lt;/p&gt;

&lt;p&gt;Similar to how I setup the Firebase "site" for the &lt;code&gt;host&lt;/code&gt; project, we can do the same here with similar commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;firebase hosting:sites:create remote1-11182024
firebase target:apply hosting remote1-11182024 &amp;lt;PROJECT_ID&amp;gt;-remote1-11182024
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then when we want to deploy, we call the Firebase command &lt;code&gt;firebase deploy --only hosting:remote1-11182024&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Going through the firebase init setup, for the &lt;code&gt;remote1&lt;/code&gt; project we get the following &lt;code&gt;firebase.json&lt;/code&gt; values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hosting"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"remote1-11182024"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"public"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ignore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"firebase.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"**/.*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"**/node_modules/**"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"rewrites"&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="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"**"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/index.html"&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;Similarly, we also get the following &lt;code&gt;.firebaserc&lt;/code&gt; file values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"projects"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;YOUR_FIREBASE_PROJECT_ID&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"targets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"&amp;lt;YOUR_FIREBASE_PROJECT_ID&amp;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="nl"&gt;"hosting"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"remote1-11182024"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"remote1-11182024"&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="nl"&gt;"etags"&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;If you look at the &lt;code&gt;remote2&lt;/code&gt; project, you'll se basically the same setup. The only difference was that the name of the remote is &lt;code&gt;remote2&lt;/code&gt; and the Firebase "site" that I created was &lt;code&gt;remote2-11182024&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Seeing everything in action
&lt;/h2&gt;

&lt;p&gt;If you want to see my sample project working locally, you can just run each project in its own terminal session. Each project runs on a different port, and if you pull up the page for the &lt;code&gt;host&lt;/code&gt; at &lt;code&gt;port 3000&lt;/code&gt; you'll see the app running and pulling in the remotes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4pyefj0537d43x1baeiz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4pyefj0537d43x1baeiz.jpg" alt="Host page loaded correctly" width="800" height="1015"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I created a sample account at &lt;a href="mailto:hello@gmail.com"&gt;hello@gmail.com&lt;/a&gt; just so I could login and verify the values were actually being passed in correctly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Note that the &lt;code&gt;host&lt;/code&gt; has props for the user that is logged in. These props are passed into the remotes without the projects needing to have the auth setup configured. This is just one example of what could be exchanged between the &lt;code&gt;host&lt;/code&gt; and remote projects. Being able to centralize configuration like this, is one of the most powerful parts of MFE projects.&lt;/p&gt;

&lt;p&gt;If you open up Chrome DevTools, you'll see that what you are seeing appears to be a regular React application:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbq1s1tjm961dwkfhb4p2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbq1s1tjm961dwkfhb4p2.jpg" alt="Host page in Chrome DevTools" width="800" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you open the network tab, however, you'll see that what was pulled in is from different endpoints:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg1nbe9v1zqrizjq5z4t9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg1nbe9v1zqrizjq5z4t9.jpg" alt="Remote1 loaded in Chrome DevTools" width="800" height="269"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl9ugqthigrfht84da2px.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl9ugqthigrfht84da2px.jpg" alt="Remote2 loaded in Chrome DevTools" width="800" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you look at the individual pages running locally, you'll see the &lt;code&gt;devBootstrap&lt;/code&gt; in action as the value for the user is mocked:&lt;/p&gt;

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

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

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

&lt;p&gt;So far I've discussed the general concepts behind MFEs, and also shown you how you can technically build your own. My general experience with MFEs are that the setup process tends to require some experimentation to get right. Fortunately, there are a fair amount of YouTube videos and blog posts that cover MFE concepts. I also was able to leverage &lt;a href="https://claude.ai/new" rel="noopener noreferrer"&gt;Claude AI&lt;/a&gt; for help getting started and working through parts of the implementation. I recommend using all of these resources (and Claude) to help you with your initial setup.&lt;/p&gt;

&lt;p&gt;Getting the values exposed by the Module Federation plugin can cause some issues. On a different MFE project I worked on, I had the incorrect name for the &lt;code&gt;remoteEntry.js&lt;/code&gt; file which we didn't see until everything was deployed and the remote would not load.&lt;/p&gt;

&lt;p&gt;Additionally, I ran into some an error about eager consumption:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Error: Shared module is not available &lt;span class="k"&gt;for &lt;/span&gt;eager consumption: webpack/sharing/consume/default/react/react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is why in the Module Federation part of my &lt;code&gt;host&lt;/code&gt; webpack config you'll see &lt;code&gt;eager: false&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;react&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;requiredVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../package.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;react&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;eager&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="c1"&gt;// Add this line&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's also important to be able to properly handle errors when loading your components. If you notice in the &lt;code&gt;host&lt;/code&gt; project, I have wrapped my "view" of the &lt;code&gt;App.tsx&lt;/code&gt; values with an &lt;code&gt;ErrorBoundary&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ErrorBoundary&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;authState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LoginForm&lt;/span&gt; &lt;span class="na"&gt;onLogin&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleLogin&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Welcome &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;authState&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;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt; &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Loading Remote 1..."&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Remote1&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;authState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt; &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Loading Remote 2..."&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Remote2&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;authState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;signOut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Logout&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ErrorBoundary&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ErrorBoundary component, is an &lt;code&gt;implementation of React's Error Boundary&lt;/code&gt; and can be seen in the &lt;code&gt;host/src/components/ErrorBoundary.tsx&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;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;Suspense&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// Error Boundary Component&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ErrorBoundary&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;hasError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;hasError&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="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;getDerivedStateFromError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;hasError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="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="nf"&gt;render&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasError&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;styles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1rem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#fef2f2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1px solid #fecaca&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;borderRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;8px&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;heading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#dc2626&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;fontWeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;marginBottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.5rem&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;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#ef4444&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;button&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;marginTop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.75rem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.5rem 1rem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#fee2e2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#dc2626&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;borderRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;4px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pointer&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;heading&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Failed to Load Component&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&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;state&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;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
                        &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                        &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reload&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                        Retry
                    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&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;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;ErrorBoundary&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Overall, I hope this post demonstrated to you how MFE's work and also showcased how you can use Firebase to build one. MFEs are a powerful concept that teams can use to manage larger projects. MFEs also have a lot of flexibility, and you can fine tune your project to match a variety of needs.&lt;/p&gt;

&lt;p&gt;In my Firebase implementation, I also hope you noticed how easy it was to get going with Firebase. I'm a long time Firebase fan, and loved how easy it was to just get setup with some basic configuration. I highly recommend Firebase for projects especially like the sample one I created for this post.&lt;/p&gt;

&lt;p&gt;I recommend going through my sample project as well as looking at other posts and YouTube videos on how MFEs work. Jack Herrington has some great videos on YouTube &lt;a href="https://youtu.be/s_Fs4AXsTnA?si=d46ajQU1szUYRzQ8" rel="noopener noreferrer"&gt;like this one&lt;/a&gt; that MFEs with some higher level concepts and examples. Webpack also has &lt;a href="https://webpack.js.org/plugins/module-federation-plugin/" rel="noopener noreferrer"&gt;documentation on the Module Federation plugin&lt;/a&gt; that you can check out.&lt;/p&gt;

&lt;p&gt;I hope my post helped you better understand MFEs and how you can use Firebase to build them. Thanks for reading my post!&lt;/p&gt;

</description>
      <category>react</category>
      <category>firebase</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Intro to Azure Search</title>
      <dc:creator>Andrew Evans</dc:creator>
      <pubDate>Mon, 17 Jun 2024 13:12:19 +0000</pubDate>
      <link>https://forem.com/andrewevans0102/intro-to-azure-search-3mp</link>
      <guid>https://forem.com/andrewevans0102/intro-to-azure-search-3mp</guid>
      <description>&lt;p&gt;The post you are reading is cross posted from my blog site. Please see my original post at &lt;a href="https://andrewevans.dev/blog/2024-06-17-intro-to-azure-search/" rel="noopener noreferrer"&gt;andrewevans.dev&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Azure AI Search (formerly Azure Cognitive Search) enables teams to improve the process of searching and analyzing data. Azure AI Search has a wide array of capabilities. I have been using Azure Search with an application I maintain, and wanted to put together a post that covers how Azure Search works and some of its features at a high level. The following post will walkthrough setting up Azure AI Search on a dataset, and how you can start utilizing Azure AI Search for your projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does Search work?
&lt;/h2&gt;

&lt;p&gt;Before I go into how Azure Search works, it helps to understand how you would approach implementing a &lt;a href="https://learn.microsoft.com/en-us/azure/search/search-lucene-query-architecture" rel="noopener noreferrer"&gt;full text search&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Most commerce websites have a "full text search" capability, and are a good example. When you "search" with these websites, applications take in text input and attempt to match that input to data in a data source like a NoSQL database or other storage mechanism. This is done through parsing data, and attempting to find a match based on &lt;code&gt;operators&lt;/code&gt; like "==" or "!=" etc.&lt;/p&gt;

&lt;p&gt;With any search service, you have (1) data and then (2) how you query the data. Consider using Amazon.com for example. Amazon.com has a lot of data between product listings, reviews, and more. When trying to search Amazon.com your search would include products but not necessarily reviews or other information. If Amazon wanted to expand their search to include the reviews (or more), they could modify their implementation to take in reviews and attempt to search based on both their list of products and product reviews. Further, when actually conducting a search in Amazon.com, what you input will find similar values (also known as "fuzzy search"). You could also be given the opportunity to do things like filtering, where you pick specific brands or results within a price point. The combination of these options act as query parameters to refine how the search is conducted.&lt;/p&gt;

&lt;p&gt;It's also important to understand "fuzzy" searching, as that comes up a lot in search discussions. This is whereby an application searches not just on exact matches, but also similar matches within the parameters provided. For example, if you searched "soccer balls" this would return results that should include the text "soccer balls." However, if you searched "soccer balls2" without a fuzzy search you would get back results with "soccer balls2" but &lt;code&gt;not&lt;/code&gt; with "soccer balls." If you ran the same query with "soccer balls2" in a fuzzy search, you would get results that match on "soccer balls" &lt;code&gt;and&lt;/code&gt; "soccer balls2" since the terms are similar.&lt;/p&gt;

&lt;p&gt;One final thing to note when generally speaking about "search" is optimizations. The reason services like Azure Search exist is because directly searching data with mechanisms like SQL queries are not always optimal. If you're using SQL in particular, there are issues with things like resource use or needing enhancements like database indexes. As data sources get larger, scaling also becomes a challenge. Services like Azure Search exist to provide optimizations for faster performance, scaling, and much more.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does Azure AI Search work?
&lt;/h2&gt;

&lt;p&gt;Azure Search implements the fundamentals of search with additional features that help teams optimize their search experience. Azure Search has also recently integrated with Azure AI Services, Azure OpenAI, and machine learning to provide additional intelligence in search results.&lt;/p&gt;

&lt;p&gt;Azure Search starts with a data source, and then uses &lt;code&gt;indexes&lt;/code&gt; (which you define) to "crawl" the data. Crawling here meaning building a data set from that original data source that is optimized and provides (potentially) faster queries with further customizations that teams can leverage in their applications. To update the dataset, you create an &lt;code&gt;indexer&lt;/code&gt; which queries and updates the data periodically. Ultimately, you get a subset of your data that is customized based on how you want your users to interact with it. This is different than just a straight retrieval of all of your data, and can have major performance gains when doing queries.&lt;/p&gt;

&lt;p&gt;The indexes setup the ability to interact with data at the &lt;code&gt;field level&lt;/code&gt;. Each field you define in an index has different attributes for what you want to do with it. For example, if you wanted users to be able to filter a query on a specific field you would mark that field as "filterable." There are several options available. Copying the section from the &lt;a href="https://learn.microsoft.com/en-us/azure/search/search-what-is-an-index#field-attributes" rel="noopener noreferrer"&gt;field attributes&lt;/a&gt; in the Azure Search documentation:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attribute&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;"searchable"&lt;/td&gt;
&lt;td&gt;Full-text or vector searchable. Text fields are subject to lexical analysis such as word-breaking during indexing. If you set a searchable field to a value like "sunny day", internally it's split into the individual tokens "sunny" and "day".&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"filterable"&lt;/td&gt;
&lt;td&gt;Referenced in $filter queries. Filterable fields of type Edm.String or Collection(Edm.String) don't undergo word-breaking, so comparisons are for exact matches only. For example, if you set such a field f to "sunny day", $filter=f eq 'sunny' finds no matches, but $filter=f eq 'sunny day' will.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"sortable"&lt;/td&gt;
&lt;td&gt;By default the system sorts results by score, but you can configure sort based on fields in the documents. Fields of type Collection(Edm.String) can't be "sortable".&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"facetable"&lt;/td&gt;
&lt;td&gt;Typically used in a presentation of search results that includes a hit count by category (for example, hotels in a specific city). This option can't be used with fields of type Edm.GeographyPoint. Fields of type Edm.String that are filterable, "sortable", or "facetable" can be at most 32 kilobytes in length.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"key"&lt;/td&gt;
&lt;td&gt;Unique identifier for documents within the index. Exactly one field must be chosen as the key field and it must be of type Edm.String.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"retrievable"&lt;/td&gt;
&lt;td&gt;Determines whether the field can be returned in a search result. This is useful when you want to use a field (such as profit margin) as a filter, sorting, or scoring mechanism, but don't want the field to be visible to the end user. This attribute must be true for key fields.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In the next section I will go through an example setting up an index with fields.&lt;/p&gt;

&lt;h2&gt;
  
  
  An Example of Azure Search
&lt;/h2&gt;

&lt;p&gt;It helps to view an example. I'll take some data from a previous post I wrote about &lt;a href="http://localhost:3000/blog/2023-09-05-cosmos-db-python-scripting/" rel="noopener noreferrer"&gt;python scripting with Cosmos DB&lt;/a&gt; that includes a set of Electric Vehicles (EV). In that post I loaded a free dataset into a Cosmos Collection that I'll use in the following example.&lt;/p&gt;

&lt;p&gt;In order to setup the actual Azure Search instance, the index, and the indexer there are a few ways. I recommend following the &lt;a href="https://learn.microsoft.com/en-us/azure/search/search-create-service-portal" rel="noopener noreferrer"&gt;Microsoft Tutorial&lt;/a&gt; to get started.&lt;/p&gt;

&lt;p&gt;In the case of my example, I first made my Azure Search instance point to my Cosmos DB collection and do the following query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Select c.id, c.VIN, c.State, c.ModelYear, c.Make, c.Model, c._ts From c WHERE c._ts &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; @HighWaterMark ORDER BY c._ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The query I'm using here is very simple because the data is not very complex. What you select with this query will be become the dataset that runs your Azure Search instance. There are many more advanced things you could do with this query like using &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/query/concat" rel="noopener noreferrer"&gt;Cosmos DB's CONCAT statement&lt;/a&gt; to create fields that are actually combinations of values. The setup here is very flexible and can work with many scenarios.&lt;/p&gt;

&lt;p&gt;With my query all setup, next I created an index on the data that looks like the following:&lt;/p&gt;

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

&lt;p&gt;As you can see in my index, I have different properties for the different fields. Some examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I can &lt;code&gt;search&lt;/code&gt; on VIN but not &lt;code&gt;sort&lt;/code&gt; or &lt;code&gt;filter&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;I can &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;search&lt;/code&gt;, and &lt;code&gt;sort&lt;/code&gt; on State, Make, and Model&lt;/li&gt;
&lt;li&gt;I can only &lt;code&gt;retrieve&lt;/code&gt; id&lt;/li&gt;
&lt;li&gt;I can only &lt;code&gt;filter&lt;/code&gt; and &lt;code&gt;search&lt;/code&gt; based on &lt;code&gt;ModelYear&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the index in place, I also defined an indexer that I just run manually. I could alternatively have defined this to run on a schedule.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;Note I called my datasource "Toddy" as a fun reference to my son. I think it made the datasource better.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Azure provides information about the runs of your indexer as well with a nice graph:&lt;/p&gt;

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

&lt;p&gt;After running the index one time, I can now do queries using the &lt;a href="https://learn.microsoft.com/en-us/azure/search/search-explorer" rel="noopener noreferrer"&gt;search explorer&lt;/a&gt;. I can do an example query like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"search"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5uyw3cvyujvvwqhua4c4.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5uyw3cvyujvvwqhua4c4.jpg" alt="Azure Search 4" width="800" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In that case I just passed the wildcard character &lt;code&gt;*&lt;/code&gt; to select everything. If I wanted to do a more refined search, I could pass operators based on the different types of fields.&lt;/p&gt;

&lt;p&gt;Here's a search for all Tesla vehicles that returns their State, ModelYear, and Model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"search"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tesla"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"select"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"State, ModelYear, Model"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;If I wanted to filter the results and only get ModelYear values greater than 2021, I could do the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"search"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tesla"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"select"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"State, ModelYear, Model"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"filter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ModelYear gt 2021"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;We should also look at the data that is being returned from these queries. When I did the search with the wildcard character &lt;code&gt;*&lt;/code&gt; you'll note that the data returned looked like similar to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@search.score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"d7c852e0-0e6d-46e4-913c-f5dcc553c680"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"VIN"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1FMCU0EZXN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"State"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"WA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ModelYear"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2022&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Make"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FORD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ESCAPE"&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;The fields that were retrieved are all from the Cosmos DB collection, but the field &lt;code&gt;@search.score&lt;/code&gt; is a measure of how that result compared to the others retrieved. There are more advanced ways to look into this value and I recommend checking out the &lt;a href="https://learn.microsoft.com/en-us/azure/search/index-similarity-and-scoring" rel="noopener noreferrer"&gt;Microsoft Article on how scoring works&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Azure Faceted Search
&lt;/h2&gt;

&lt;p&gt;A more advanced concept with Search is to use &lt;a href="https://learn.microsoft.com/en-us/azure/search/search-faceted-navigation" rel="noopener noreferrer"&gt;Search Faceted Navigation&lt;/a&gt;. Facets allow you to control (server side) navigation that you would present to a Frontend that uses Azure Search.&lt;/p&gt;

&lt;p&gt;In my example of a list of EV cars, I created a second index that included some fields that use Facets:&lt;/p&gt;

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

&lt;p&gt;With that index in place, I then am able to run a query where I specify one of the Faceted values:&lt;/p&gt;

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

&lt;p&gt;If you notice in the response there is a "facets" value that includes the Faceted field and a total. This provides data that could be used by a Frontend to show totals for specific values within the query result.&lt;/p&gt;

&lt;p&gt;To provide a more refined search, I can then specify a filter to use with the facets:&lt;/p&gt;

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

&lt;p&gt;There is a lot more that you can do with Facets. I recommend you check out the &lt;a href="https://learn.microsoft.com/en-us/azure/search/search-faceted-navigation" rel="noopener noreferrer"&gt;Microsoft Documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Calling Azure Search from an API
&lt;/h2&gt;

&lt;p&gt;Azure has SDKs that are available for interacting with the Search Service. The &lt;a href="https://www.nuget.org/packages/Azure.Search.Documents/" rel="noopener noreferrer"&gt;Azure Search Nuget Package&lt;/a&gt; provides the integrations necessary for you to use Azure Search with your C# applications.&lt;/p&gt;

&lt;p&gt;Once you have created your project and included the Azure Search Nuget Package, you can call the service directly in your projects. Just to play around with this locally, I modified the &lt;a href="https://github.com/Azure-Samples/azure-search-dotnet-samples/tree/main/quickstart/v11" rel="noopener noreferrer"&gt;Azure quickstart for .NET&lt;/a&gt; to suit the vehicles collection I had been working with.&lt;/p&gt;

&lt;p&gt;I first created a class to represent the vehicles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Text.Json.Serialization&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Azure.Search.Documents.Indexes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Azure.Search.Documents.Indexes.Models&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;AzureSearch.Quickstart&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Vehicle&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;SimpleField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IsKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IsFilterable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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;SearchableField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IsSortable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;VIN&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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;SearchableField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AnalyzerName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LexicalAnalyzerName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EnLucene&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;ModelYear&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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;SearchableField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IsFilterable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IsSortable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Make&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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;SearchableField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IsFilterable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IsSortable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Model&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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;I then modified the program's &lt;code&gt;Main&lt;/code&gt; method to setup the package with the proper configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Text.Json&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Azure&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Azure.Search.Documents&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Azure.Search.Documents.Indexes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Azure.Search.Documents.Indexes.Models&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Azure.Search.Documents.Models&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;AzureSearch.Quickstart&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// setup the configuration based on values from Azure&lt;/span&gt;
            &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;serviceName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;AZURE_SEARCH_INSTANCE_NAME&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;AZURE_SEARCH_API_KEY&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;indexName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;AZURE_SEARCH_INDEX_NAME&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// create the actual client&lt;/span&gt;
            &lt;span class="n"&gt;Uri&lt;/span&gt; &lt;span class="n"&gt;serviceEndpoint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"https://&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;serviceName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.search.windows.net/"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;AzureKeyCredential&lt;/span&gt; &lt;span class="n"&gt;credential&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AzureKeyCredential&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;SearchIndexClient&lt;/span&gt; &lt;span class="n"&gt;adminClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SearchIndexClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serviceEndpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;SearchClient&lt;/span&gt; &lt;span class="n"&gt;srchclient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SearchClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serviceEndpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indexName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// call the queries wrapped in a method&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Starting queries...\n"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;RunQueries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;srchclient&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// End the program&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{0}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Complete. Press any key to end this program...\n"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadKey&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;The example program writes values out to the console. I modified it to only show the values that were not null since the queries retrieved different sets of values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Write search results to console&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;WriteDocuments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SearchResults&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Vehicle&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;searchResults&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SearchResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Vehicle&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;searchResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetResults&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Vehicle&lt;/span&gt; &lt;span class="n"&gt;vehicle&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vehicle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetType&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Reflection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PropertyInfo&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;properties&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetProperties&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"{"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Reflection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PropertyInfo&lt;/span&gt; &lt;span class="n"&gt;property&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vehicle&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;value&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
                &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;$"\"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;\":\"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&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="n"&gt;output&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;"}"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&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;With the client created and the output method setup, I then modified the queries in the &lt;code&gt;RunQueries&lt;/code&gt; method similar to how we did in the search explorer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// here is the first query we did with selecting all the values&lt;/span&gt;
&lt;span class="n"&gt;SearchOptions&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;SearchResults&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Vehicle&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// specify options like query size&lt;/span&gt;
&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SearchOptions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;IncludeTotalCount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Filter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;OrderBy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;Size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"VIN"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"State"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ModelYear"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Make"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Model"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// passing in a wildcard character like we did in the search explorer&lt;/span&gt;
&lt;span class="c1"&gt;// also note I had to pass in a class based implementation of the vehicle values&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;srchclient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Vehicle&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above will result in the following console output:&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="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"VIN"&lt;/span&gt;:&lt;span class="s2"&gt;"1FMCU0EZXN"&lt;/span&gt;,&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2022"&lt;/span&gt;,&lt;span class="s2"&gt;"Make"&lt;/span&gt;:&lt;span class="s2"&gt;"FORD"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"ESCAPE"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"VIN"&lt;/span&gt;:&lt;span class="s2"&gt;"1G1FW6S03J"&lt;/span&gt;,&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2018"&lt;/span&gt;,&lt;span class="s2"&gt;"Make"&lt;/span&gt;:&lt;span class="s2"&gt;"CHEVROLET"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"BOLT EV"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"VIN"&lt;/span&gt;:&lt;span class="s2"&gt;"1FADP5CU8F"&lt;/span&gt;,&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2015"&lt;/span&gt;,&lt;span class="s2"&gt;"Make"&lt;/span&gt;:&lt;span class="s2"&gt;"FORD"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"C-MAX"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"VIN"&lt;/span&gt;:&lt;span class="s2"&gt;"WB523CF03P"&lt;/span&gt;,&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2023"&lt;/span&gt;,&lt;span class="s2"&gt;"Make"&lt;/span&gt;:&lt;span class="s2"&gt;"BMW"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"IX"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"VIN"&lt;/span&gt;:&lt;span class="s2"&gt;"1G1RC6S54J"&lt;/span&gt;,&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2018"&lt;/span&gt;,&lt;span class="s2"&gt;"Make"&lt;/span&gt;:&lt;span class="s2"&gt;"CHEVROLET"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"VOLT"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"VIN"&lt;/span&gt;:&lt;span class="s2"&gt;"WBY1Z4C56G"&lt;/span&gt;,&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2016"&lt;/span&gt;,&lt;span class="s2"&gt;"Make"&lt;/span&gt;:&lt;span class="s2"&gt;"BMW"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"I3"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"VIN"&lt;/span&gt;:&lt;span class="s2"&gt;"1G1RD6E44D"&lt;/span&gt;,&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2013"&lt;/span&gt;,&lt;span class="s2"&gt;"Make"&lt;/span&gt;:&lt;span class="s2"&gt;"CHEVROLET"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"VOLT"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"VIN"&lt;/span&gt;:&lt;span class="s2"&gt;"1G1FW6S00J"&lt;/span&gt;,&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2018"&lt;/span&gt;,&lt;span class="s2"&gt;"Make"&lt;/span&gt;:&lt;span class="s2"&gt;"CHEVROLET"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"BOLT EV"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"VIN"&lt;/span&gt;:&lt;span class="s2"&gt;"KNDCC3LG2L"&lt;/span&gt;,&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2020"&lt;/span&gt;,&lt;span class="s2"&gt;"Make"&lt;/span&gt;:&lt;span class="s2"&gt;"KIA"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"NIRO"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"VIN"&lt;/span&gt;:&lt;span class="s2"&gt;"WAUTPBFF3H"&lt;/span&gt;,&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2017"&lt;/span&gt;,&lt;span class="s2"&gt;"Make"&lt;/span&gt;:&lt;span class="s2"&gt;"AUDI"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"A3"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the second query I did looking for Tesla vehicles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SearchOptions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;IncludeTotalCount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Filter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;OrderBy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;Size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"State"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ModelYear"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Model"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;srchclient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Vehicle&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Tesla"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Writing the values out to the console, resulted in the following values:&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="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2023"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"MODEL Y"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2023"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"MODEL 3"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2019"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"MODEL S"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2022"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"MODEL Y"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2023"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"MODEL Y"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2021"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"MODEL 3"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2019"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"MODEL 3"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2022"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"MODEL Y"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2019"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"MODEL 3"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2023"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"MODEL Y"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another example query I had was Tesla cars older than 2021.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SearchOptions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Filter&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ModelYear gt 2021"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"State"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ModelYear"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Model"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;srchclient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Vehicle&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Tesla"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Writing this out to the console resulted in output similar to the following:&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="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2023"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"MODEL Y"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2023"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"MODEL 3"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2022"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"MODEL Y"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2023"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"MODEL Y"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2022"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"MODEL Y"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2023"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"MODEL Y"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2023"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"MODEL X"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2022"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"MODEL Y"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2022"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"MODEL 3"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"State"&lt;/span&gt;:&lt;span class="s2"&gt;"WA"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2023"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"MODEL Y"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also modified the configuration value to use my second index with Facets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// setup options&lt;/span&gt;
&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SearchOptions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Filter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;IncludeTotalCount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// add Facet value of Make&lt;/span&gt;
&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Facets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Make"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// specify fields to select&lt;/span&gt;
&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Make"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ModelYear"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Model"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"VIN"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// do the actual search and write to the console&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;srchclient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Vehicle&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;WriteDocuments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the console output I also had to modify the way it parsed the &lt;code&gt;facets&lt;/code&gt; in the response with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;searchResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Facets&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;searchResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Facets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"FACETS"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;makeFacets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;searchResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Facets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Make"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;facets&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;makeFacets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"  Value: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;facets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, Count: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;facets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&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;With everything setup for Facets, the resulting console output was the following values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;FACETS
  Value: TESLA, Count: 3293
  Value: NISSAN, Count: 941
  Value: CHEVROLET, Count: 752
  Value: BMW, Count: 453
  Value: FORD, Count: 399
  Value: KIA, Count: 384
  Value: TOYOTA, Count: 345
  Value: JEEP, Count: 302
  Value: VOLVO, Count: 272
  Value: VOLKSWAGEN, Count: 251

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"VIN"&lt;/span&gt;:&lt;span class="s2"&gt;"1FMCU0EZXN"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2022"&lt;/span&gt;,&lt;span class="s2"&gt;"Make"&lt;/span&gt;:&lt;span class="s2"&gt;"FORD"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"ESCAPE"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"VIN"&lt;/span&gt;:&lt;span class="s2"&gt;"1G1FW6S03J"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2018"&lt;/span&gt;,&lt;span class="s2"&gt;"Make"&lt;/span&gt;:&lt;span class="s2"&gt;"CHEVROLET"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"BOLT EV"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"VIN"&lt;/span&gt;:&lt;span class="s2"&gt;"1FADP5CU8F"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2015"&lt;/span&gt;,&lt;span class="s2"&gt;"Make"&lt;/span&gt;:&lt;span class="s2"&gt;"FORD"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"C-MAX"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"VIN"&lt;/span&gt;:&lt;span class="s2"&gt;"WB523CF03P"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2023"&lt;/span&gt;,&lt;span class="s2"&gt;"Make"&lt;/span&gt;:&lt;span class="s2"&gt;"BMW"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"IX"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"VIN"&lt;/span&gt;:&lt;span class="s2"&gt;"1G1RC6S54J"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2018"&lt;/span&gt;,&lt;span class="s2"&gt;"Make"&lt;/span&gt;:&lt;span class="s2"&gt;"CHEVROLET"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"VOLT"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"VIN"&lt;/span&gt;:&lt;span class="s2"&gt;"WBY1Z4C56G"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2016"&lt;/span&gt;,&lt;span class="s2"&gt;"Make"&lt;/span&gt;:&lt;span class="s2"&gt;"BMW"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"I3"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"VIN"&lt;/span&gt;:&lt;span class="s2"&gt;"1G1RD6E44D"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2013"&lt;/span&gt;,&lt;span class="s2"&gt;"Make"&lt;/span&gt;:&lt;span class="s2"&gt;"CHEVROLET"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"VOLT"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"VIN"&lt;/span&gt;:&lt;span class="s2"&gt;"1G1FW6S00J"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2018"&lt;/span&gt;,&lt;span class="s2"&gt;"Make"&lt;/span&gt;:&lt;span class="s2"&gt;"CHEVROLET"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"BOLT EV"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"VIN"&lt;/span&gt;:&lt;span class="s2"&gt;"KNDCC3LG2L"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2020"&lt;/span&gt;,&lt;span class="s2"&gt;"Make"&lt;/span&gt;:&lt;span class="s2"&gt;"KIA"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"NIRO"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"VIN"&lt;/span&gt;:&lt;span class="s2"&gt;"WAUTPBFF3H"&lt;/span&gt;,&lt;span class="s2"&gt;"ModelYear"&lt;/span&gt;:&lt;span class="s2"&gt;"2017"&lt;/span&gt;,&lt;span class="s2"&gt;"Make"&lt;/span&gt;:&lt;span class="s2"&gt;"AUDI"&lt;/span&gt;,&lt;span class="s2"&gt;"Model"&lt;/span&gt;:&lt;span class="s2"&gt;"A3"&lt;/span&gt;,&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are many more things you could do here, but I kept these queries necessarily simple just to showcase how this works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this post I hope you learned some details about how Azure Search works. I introduced search as a concept, and then went into detail about how search works with Azure. I covered an example that created an Azure Search instance and ran sample queries with the search explorer. I also included some examples of how you could use the &lt;a href="https://www.nuget.org/packages/Azure.Search.Documents/" rel="noopener noreferrer"&gt;Azure Search Nuget Package&lt;/a&gt; to query Azure Search in a C# .NET application. I only covered a high level of how to get started, and definitely recommend reviewing the various Microsoft tutorials and documentation that work with search. Other features that are worth noting are &lt;a href="https://learn.microsoft.com/en-us/azure/search/search-add-autocomplete-suggestions" rel="noopener noreferrer"&gt;Autocomplete with Azure Search&lt;/a&gt; as well as several other advanced search options you can find in the &lt;a href="https://learn.microsoft.com/en-us/azure/search/" rel="noopener noreferrer"&gt;official Azure Search documentation&lt;/a&gt;. Azure Search has a lot of potential to provide good solutions to searching needs. Thanks for reading my post!&lt;/p&gt;

</description>
      <category>azure</category>
    </item>
    <item>
      <title>How to use Cypress for E2E with React</title>
      <dc:creator>Andrew Evans</dc:creator>
      <pubDate>Mon, 15 Mar 2021 09:38:53 +0000</pubDate>
      <link>https://forem.com/andrewevans0102/how-to-use-cypress-for-e2e-with-react-565f</link>
      <guid>https://forem.com/andrewevans0102/how-to-use-cypress-for-e2e-with-react-565f</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Flamp-3489395_1920.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Flamp-3489395_1920.jpg" alt="Cover Image" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The cover image was originally copied from &lt;a href="https://pixabay.com/photos/lamp-light-lighting-light-bulb-3489395/" rel="noopener noreferrer"&gt;https://pixabay.com/photos/lamp-light-lighting-light-bulb-3489395/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With any Frontend application, End to End (e2e) testing can really improve maintenance and the Software Development Life Cycle (SDLC) of the application. E2E allows you to quickly verify changes, and also works to document features in your application.&lt;/p&gt;

&lt;p&gt;There are a few options for E2E frameworks today. &lt;a href="https://www.cypress.io/" rel="noopener noreferrer"&gt;Cypress&lt;/a&gt; is one of the newer and more exciting options for building E2E tests in your application.&lt;/p&gt;

&lt;p&gt;Cypress is a great option for E2E because it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;runs in the same event loop as your frontend project (rather than externally "knocking on the door" like E2E &lt;a href="https://www.selenium.dev/documentation/en/webdriver/" rel="noopener noreferrer"&gt;Selenium&lt;/a&gt; based projects do)&lt;/li&gt;
&lt;li&gt;Works with any Frontend project (install Cypress as a dependency and the test runner will automatically detect the corresponding configuration files)&lt;/li&gt;
&lt;li&gt;All of the E2E is written in JavaScript (no need to have &lt;code&gt;.feature&lt;/code&gt; or other associated files)&lt;/li&gt;
&lt;li&gt;Cypress provides a hot reloading test runner allowing you to develop your tests in a very similar way to how you do local development already&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The only limitations of Cypress is that it works with &lt;a href="https://www.cypress.io/blog/2020/02/06/introducing-firefox-and-edge-support-in-cypress-4-0/#header" rel="noopener noreferrer"&gt;Chrome, Firefox, Electron, and the Edge browser&lt;/a&gt;. There are plans for it to support more browsers in the future. It is important to note, however, that those 4 browsers take up a big market share of the browser world and solve many usecases. Each project has different needs, but these 4 browsers provide a lot of potential coverage of E2E tests for Frontend projects.&lt;/p&gt;

&lt;p&gt;Cypress has a load of features and a great community supporting it. I highly recommend &lt;a href="https://docs.cypress.io/guides/overview/why-cypress.html#In-a-nutshell" rel="noopener noreferrer"&gt;checking out their docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this post I'm going to introduce Cypress and how you can use it with React. If you'd like to follow along, I've created a GitHub repo that has all of the examples I cover at &lt;a href="https://www.github.com/andrewevans0102/cypress-react" rel="noopener noreferrer"&gt;https://www.github.com/andrewevans0102/cypress-react&lt;/a&gt;. The example I used also is a rebuild of a previous project I used for my post &lt;a href="https://www.newline.co/@AndrewEvans/how-to-get-started-with-cypress--0bed3a8b" rel="noopener noreferrer"&gt;How to get started with Cypress&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I also created a YouTube video where I walk through this same sample project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=FkJMsBEG5LA" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fugbjrpcs0us76c8rufz3.jpg" alt="YouTube video" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample Application
&lt;/h2&gt;

&lt;p&gt;Before we dive into using Cypress, I just want to explain how the sample application works. You can reach the sample application at &lt;a href="https://www.github.com/andrewevans0102/cypress-react" rel="noopener noreferrer"&gt;https://www.github.com/andrewevans0102/cypress-react&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-03-15-at-6.00.09-am.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-03-15-at-6.00.09-am.png" alt="Sample Application" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The application has three major sections which have examples for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Forms&lt;/li&gt;
&lt;li&gt;Lists&lt;/li&gt;
&lt;li&gt;Network Requests&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In our Cypress Setup we're going to walkthrough building E2E tests for all three of these pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Cypress
&lt;/h2&gt;

&lt;p&gt;There are multiple ways to install Cypress. If you &lt;a href="https://docs.cypress.io/guides/getting-started/installing-cypress.html#System-requirements" rel="noopener noreferrer"&gt;check out the docs&lt;/a&gt;, you'll see that you can install it with &lt;code&gt;npm&lt;/code&gt;, &lt;code&gt;yarn&lt;/code&gt;, or even a manual install of the binary.&lt;/p&gt;

&lt;p&gt;In my project I used npm, so I installed it as a &lt;code&gt;dev dependency&lt;/code&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install cypress --save-dev

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

&lt;/div&gt;



&lt;p&gt;Once you do the &lt;code&gt;npm install&lt;/code&gt; you should see a &lt;code&gt;cypress&lt;/code&gt; folder and a &lt;code&gt;cypress.json&lt;/code&gt; file created in your project's folder:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-03-15-at-6.11.54-am.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-03-15-at-6.11.54-am.png" alt="Folders" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please notice the "examples" folder, this is something I created and just copied the generated example files from &lt;code&gt;integrations&lt;/code&gt; over to their own folder for simplicity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-03-15-at-6.12.00-am.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-03-15-at-6.12.00-am.png" alt="JSON File" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The directories and files that are built contain different configuration that we'll use to build E2E with Cypress. They all correspond to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;fixtures&lt;/code&gt; is where you build mocks or stubbed responses for your tests&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;integration&lt;/code&gt; is where you place your actual test &lt;code&gt;.spec.js&lt;/code&gt; files by default.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;plugins&lt;/code&gt; allow you to extend Cypress behavior&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;screenshots&lt;/code&gt; and &lt;code&gt;videos&lt;/code&gt; are where the test runner will store visual copies of test runs (more on that in the next sections)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;support&lt;/code&gt; allows you to define "commands" or boil plate behavior you can reference in your tests avoiding the need to repeat startup tasks like login or similar flows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you've got Cypress installed, you can see your first interaction with Cypress by calling "open" with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./node_modules/.bin/cypress open

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  First Tests
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-03-15-at-6.31.09-am.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-03-15-at-6.31.09-am.png" alt="Local Window" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you first install Cypress, you'll notice in the &lt;code&gt;integrations&lt;/code&gt; folder that there is a set of examples. These are really useful because you can see a first interaction with the test runner. These examples hit the Cypress "kitchen sink" site, so there is no configuration required to run them. If you want to go ahead and play with them, you can use the &lt;code&gt;cypress open&lt;/code&gt; command that was at the end of the previous section to see the test runner in action.&lt;/p&gt;

&lt;p&gt;I normally go ahead and copy the "examples" over to its own directory. Assuming you've done that, the next step is to configure some scripts that automate working with Cypress.&lt;/p&gt;

&lt;p&gt;I recommend creating the following npm scripts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
"scripts": {
    "cypress-open": "./node_modules/.bin/cypress open",
    "cypress-local": "concurrently \"npm run start\" \"npm run cypress-open\"",
    "cypress-run": "./node_modules/.bin/cypress run",
    "cypress-ci": "start-server-and-test \"npm run start\" http://localhost:3000 \"npm run cypress-run\""
}
}

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

&lt;/div&gt;



&lt;p&gt;Let's talk about what they do:&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;cypress-open&lt;/code&gt; opens the test runner by itself&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;cypress-local&lt;/code&gt; runs the test runner and the application locally (interactive)&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;cypress-run&lt;/code&gt; runs the test runner in CI (headless)&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;cypress-ci&lt;/code&gt; runs the application and the test runner in CI (headless)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please note CI refers to "Continous Integration" and is just referring to a build pipeline&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You'll also need to go ahead and install &lt;a href="https://www.npmjs.com/package/concurrently" rel="noopener noreferrer"&gt;concurrently&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/start-server-and-test" rel="noopener noreferrer"&gt;start-server-and-test&lt;/a&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install concurrently
npm install start-server-and-test

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

&lt;/div&gt;



&lt;p&gt;As I stated in the intro, Cypress runs in the same event loop as your application. This means that when you run your Cypress tests, you'll need to use some mechanism to run your application alongside the test runner. The use of &lt;code&gt;concurrently&lt;/code&gt; and &lt;code&gt;start-server-and-test&lt;/code&gt; allows for this behavior. When running locally &lt;code&gt;concurrently&lt;/code&gt; keeps your application running alongside the test runner. When running in CI, the use of &lt;code&gt;start-server-and-test&lt;/code&gt; will shut down your application and the test runner when the tests have completed.&lt;/p&gt;

&lt;p&gt;Let's write our first test to see the "local" run of this in action.&lt;/p&gt;

&lt;p&gt;Go over to the &lt;code&gt;integrations&lt;/code&gt; folder and create a file &lt;code&gt;first_test.spec.js&lt;/code&gt; with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;describe("First Test", () =&amp;gt; {
  it("should visit home page", () =&amp;gt; {
    cy.visit("http://localhost:3000/home-page");
  });
});

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

&lt;/div&gt;



&lt;p&gt;If you notice, this is following the standard &lt;a href="https://mochajs.org/" rel="noopener noreferrer"&gt;mocha&lt;/a&gt; and &lt;a href="https://jasmine.github.io/" rel="noopener noreferrer"&gt;jasmine&lt;/a&gt; syntax. This is nice because if you're familiar with other testing frameworks, there isn't a lot for you to learn beyond just building your tests and getting used to the &lt;code&gt;cy&lt;/code&gt; test runner object.&lt;/p&gt;

&lt;p&gt;In this first test, we're just visiting the sample applications home page. Once you've created your &lt;code&gt;first_test.spec.js&lt;/code&gt; file, go ahead and run &lt;code&gt;npm run cypress-local&lt;/code&gt; to see the test run.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-03-15-at-6.36.33-am.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-03-15-at-6.36.33-am.png" alt="First Run" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you do the first run, you'll note that by default Cypress will open a session of &lt;code&gt;Chrome&lt;/code&gt; to actually test your application. The test runner then provides details on each step that is run, and you can even do "time travel" when you click through the different steps that ran.&lt;/p&gt;

&lt;h2&gt;
  
  
  Forms Test
&lt;/h2&gt;

&lt;p&gt;So now that we have our first test running, let's go ahead and create our forms page test. Go to the &lt;code&gt;integrations&lt;/code&gt; folder and create &lt;code&gt;form.spec.js&lt;/code&gt; like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;describe('Form Test', () =&amp;gt; {
  it('should visit home page', () =&amp;gt; {
    cy.visit('/home-page');
  });

  it('should visit home page and click start', () =&amp;gt; {
    cy.visit('/home-page');
    cy.get('#startButton').click();
    cy.get('h1').should('contain', 'Learn Cypress');
  });

  it('should go to the forms page and enter login information', () =&amp;gt; {
    cy.visit('/home-page');
    cy.get('#startButton').click();
    cy.get('h1').should('contain', 'Learn Cypress');
    cy.get('#formsButton').click();
    cy.get("#email").type("HanSolo@gmail.com");
    cy.get("#password").type("password");
    cy.get("#submitButton").click();
    cy.on("window:alert", (str) =&amp;gt; {
      expect(str).to.equal(
        "successfully entered input with email HanSolo@gmail.com and password password"
      );
    });
  });
});

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

&lt;/div&gt;



&lt;p&gt;Also, go ahead and modify the &lt;code&gt;cypress.json&lt;/code&gt; file to have the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{ "baseUrl": "http://localhost:3000" }

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

&lt;/div&gt;



&lt;p&gt;What we just did there is created our first &lt;code&gt;environment variable&lt;/code&gt; within Cypress. The &lt;code&gt;cypress.json&lt;/code&gt; file allows you to create variables you can reference in your tests. The &lt;code&gt;baseUrl&lt;/code&gt; is a built in variable, but you can &lt;a href="https://docs.cypress.io/guides/guides/environment-variables.html#Setting" rel="noopener noreferrer"&gt;create your own custom ones as well&lt;/a&gt;. By having the &lt;code&gt;baseUrl&lt;/code&gt; defined, we can modify our "visit the homepage test" to be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  it('should visit home page', () =&amp;gt; {
    cy.visit('/home-page');
  });

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

&lt;/div&gt;



&lt;p&gt;instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  it("should visit home page", () =&amp;gt; {
    cy.visit("http://localhost:3000/home-page");
  });

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

&lt;/div&gt;



&lt;p&gt;If you go ahead and run &lt;code&gt;npm run cypress-local&lt;/code&gt; then you should see the forms test run. If you hadn't stopped Cypress or the local application, you should've seen the test runner automatically load in the files and reload the page. This is one of the best parts of Cypress because it allows for "hot reloading" as you develop your tests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-03-15-at-6.44.33-am.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-03-15-at-6.44.33-am.png" alt="Forms Output" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you notice in the code, we're passing commands to the Cypress test runner object &lt;code&gt;cy&lt;/code&gt; like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cy.get('#startButton').click();

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

&lt;/div&gt;



&lt;p&gt;What we are doing here is identifying the object on the page, and then passing events to it. We could also get information as you see with the asserts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cy.get('h1').should('contain', 'Learn Cypress');

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

&lt;/div&gt;



&lt;p&gt;This is all similar behavior to the traditional:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;arrange&lt;/code&gt; = setup your test environment&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;act&lt;/code&gt; = run the actual test&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;assert&lt;/code&gt; = verify the output result&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There is a lot of documentation on how to setup tests and interact with the &lt;code&gt;cy&lt;/code&gt; object. I recommend checking out the guide on &lt;a href="https://docs.cypress.io/guides/getting-started/writing-your-first-test.html" rel="noopener noreferrer"&gt;writing your first tests&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lists Test
&lt;/h2&gt;

&lt;p&gt;So now we can also try testing the &lt;code&gt;lists&lt;/code&gt; page. Create a file &lt;code&gt;list.spec.js&lt;/code&gt; in the &lt;code&gt;integrations&lt;/code&gt; folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;describe("List Test", () =&amp;gt; {
  it("should go to the list page and add a value", () =&amp;gt; {
    cy.visit("/home-page");
    cy.get("#startButton").click();
    cy.get("h1").should("contain", "Learn Cypress");
    cy.get("#listsButton").click();
    cy.get("#createInput").type("use the force Luke!");
    cy.get("#createButton").click();
    cy.get("li").eq(4).should("contain", "use the force Luke!");
  });

  it("should go to the list page and delete a value", () =&amp;gt; {
    cy.visit("/home-page");
    cy.get("#startButton").click();
    cy.get("h1").should("contain", "Learn Cypress");
    cy.get("#listsButton").click();
    cy.get("#createInput").type("use the force Luke!");
    cy.get("#createButton").click();
    cy.get("li").eq(4).should("contain", "use the force Luke!");
    cy.get(":nth-child(5) &amp;gt; .btn").click();
    cy.get("[data-cy=listValues]").children().should("have.length", 4);
  });
});

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

&lt;/div&gt;



&lt;p&gt;Once you've ran it, you should see something like the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-03-15-at-6.53.44-am.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-03-15-at-6.53.44-am.png" alt="Lists Output" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you run &lt;code&gt;npm run cypress-local&lt;/code&gt; now, you can run the tests on the lists page. This is similar to the way that built our forms tests, with the additonal step that you notice we can count values as you see here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    cy.get(":nth-child(5) &amp;gt; .btn").click();
    cy.get("[data-cy=listValues]").children().should("have.length", 4);

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

&lt;/div&gt;



&lt;p&gt;This just highlights one of the cool things you can do with the &lt;code&gt;cy&lt;/code&gt; runner. Its particularly useful with lists since you'll often need to see the length of a list or if a value is present within the list in a frontend project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Network Requests
&lt;/h2&gt;

&lt;p&gt;With any Frontend application, network requests are always a key part of any workflow. You need to interact with the backend to get or push data.&lt;/p&gt;

&lt;p&gt;Let's create our network requests test in the &lt;code&gt;integrations&lt;/code&gt; folder by creating a file called &lt;code&gt;network.spec.js&lt;/code&gt; with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;describe("Network Requests Page Test", () =&amp;gt; {
  beforeEach(() =&amp;gt; {
    // server starts to listen for http calls
    cy.server();
    // create route that cypress will listen for, here it is the films endpoint of the SWAPI
    cy.route("GET", "https://swapi.dev/api/films/**").as("films");
  });

  it("should go to the network requests page and select a movie", () =&amp;gt; {
    cy.visit("/home-page");
    cy.get("#startButton").click();
    cy.get("h1").should("contain", "Learn Cypress");
    cy.get("#networkButton").click();
    cy.get("#movieSelect").select("A New Hope (1)");
    cy.get("#movieTitle").should("contain", "A New Hope");
    cy.get("#episodeNumber").should("contain", 4);
  });

  it("should go to the network requests page and verify the HTTP payload called", () =&amp;gt; {
    cy.visit("/home-page");
    cy.get("#startButton").click();
    cy.get("h1").should("contain", "Learn Cypress");
    cy.get("#networkButton").click();
    cy.get("#movieSelect").select("A New Hope (1)");
    // await the response from the SWAPI http call
    cy.wait("@films").then((films) =&amp;gt; {
      expect(films.response.body.title).to.equal("A New Hope");
    });
    cy.get("#movieTitle").should("contain", "A New Hope");
    cy.get("#episodeNumber").should("contain", 4);
  });
});

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

&lt;/div&gt;



&lt;p&gt;If you run it, you should see the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-03-15-at-6.57.12-am.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-03-15-at-6.57.12-am.png" alt="Network Output" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this test, you'll notice that we have a &lt;code&gt;beforeEach&lt;/code&gt; block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  beforeEach(() =&amp;gt; {
    // server starts to listen for http calls
    cy.server();
    // create route that cypress will listen for, here it is the films endpoint of the SWAPI
    cy.route("GET", "https://swapi.dev/api/films/**").as("films");
  });

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

&lt;/div&gt;



&lt;p&gt;This is standard with &lt;code&gt;mocha&lt;/code&gt; and &lt;code&gt;jasmine&lt;/code&gt; as it sets up the test runner before actually exercising the tests. Notice the use of the &lt;code&gt;cy.server&lt;/code&gt; object. This allows Cypress to listen for network calls and specficially this test is looking for the "swapi.dev" API call with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cy.route("GET", "https://swapi.dev/api/films/**").as("films");
  });

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

&lt;/div&gt;



&lt;p&gt;Then in the actual test runs, notice that there is a &lt;code&gt;cy.wait&lt;/code&gt; which waits for the API call to complete to verify the results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // await the response from the SWAPI http call
    cy.wait("@films").then((films) =&amp;gt; {
      expect(films.response.body.title).to.equal("A New Hope");
    });

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

&lt;/div&gt;



&lt;p&gt;This is very powerful in that it will allow you to test the payload of your API calls, and allows the test runner to be versatile enough to not only deal with the DOM on your page, but also the proper payloads that the HTTP calls should be returning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Commands
&lt;/h2&gt;

&lt;p&gt;So up until this point, all of our tests have had something like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    cy.visit("/home-page");
    cy.get("#startButton").click();
    cy.get("h1").should("contain", "Learn Cypress");
    cy.get("#networkButton").click();

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

&lt;/div&gt;



&lt;p&gt;This is what I would consider "boiler plate" in that you always have to visit the home page and click one of the buttons to interact with the applicable page.&lt;/p&gt;

&lt;p&gt;Cypress allows you to reduce that boilerplate by creating &lt;code&gt;commands&lt;/code&gt; in the &lt;code&gt;support&lt;/code&gt; folder. If you go ahead and open up the file &lt;code&gt;cypress/support/commands.js&lt;/code&gt; you'll notice that there is some docs pointing to the &lt;a href="https://on.cypress.io/custom-commands" rel="noopener noreferrer"&gt;commands guide&lt;/a&gt;. In Cypress, you can build &lt;code&gt;commands&lt;/code&gt; which are basically just aliases to a set of steps. If you build a command here, you can then reference it in your tests and avoid having to copy and paste a lot.&lt;/p&gt;

&lt;p&gt;Go ahead and add the following to the &lt;code&gt;commands.js&lt;/code&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;Cypress.Commands.add("start", () =&amp;gt; {
  cy.visit("/home-page");
  cy.get("#startButton").click();
  cy.get("h1").should("contain", "Learn Cypress");
  cy.get("#formsButton").should("contain", "Forms");
  cy.get("#listsButton").should("contain", "Lists");
  cy.get("#networkButton").should("contain", "Network Requests");
});

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

&lt;/div&gt;



&lt;p&gt;Here we create a &lt;code&gt;start&lt;/code&gt; command which has the flow up to the &lt;code&gt;content&lt;/code&gt; page. It then verifies that the correct values are present for the button labels.&lt;/p&gt;

&lt;p&gt;We can then go back to our original Forms Test file (&lt;code&gt;forst.spec.js&lt;/code&gt;) and remove the:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    cy.visit('/home-page');
    cy.get('#startButton').click();
    cy.get('h1').should('contain', 'Learn Cypress');

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

&lt;/div&gt;



&lt;p&gt;and add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  before(() =&amp;gt; {
    cy.start();
  });

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

&lt;/div&gt;



&lt;p&gt;This references the &lt;code&gt;start&lt;/code&gt; command we created. Now if you run the test, you'll see the &lt;code&gt;start&lt;/code&gt; command and test has run without you needing to reference in the associated spec file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-03-15-at-7.06.50-am.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-03-15-at-7.06.50-am.png" alt="Command Output" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Running Cypress in CI
&lt;/h2&gt;

&lt;p&gt;So the last thing I wanted to cover was what happens when you run Cypress in CI. In the beginning sections you notice we created a &lt;code&gt;cypress-ci&lt;/code&gt; npm script which called &lt;code&gt;cypress run&lt;/code&gt; instead of &lt;code&gt;cypress open&lt;/code&gt;. This is Cypress mechanism to run "headless" in your project pipeline.&lt;/p&gt;

&lt;p&gt;If you go ahead and take the tests we've written so far, you can run &lt;code&gt;cypress-ci&lt;/code&gt; to see the output. First stop your app and Cypress (if you haven't already done so) and then run &lt;code&gt;npm run cypress-ci&lt;/code&gt; to see the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-03-15-at-7.10.29-am.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-03-15-at-7.10.29-am.png" alt="CI Output" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is all logged runs of all your spec files. Up until now, you would run each &lt;code&gt;spec&lt;/code&gt; file individually with the test runner GUI. Now in CI, Cypress will run all of your spec files with console output that you can keep in your respective CI system.&lt;/p&gt;

&lt;p&gt;You'll also note that there are &lt;code&gt;mp4&lt;/code&gt; files stored in the &lt;code&gt;videos&lt;/code&gt; folder:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-03-15-at-7.12.30-am.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-03-15-at-7.12.30-am.png" alt="Video Output" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These are live runs of your CI tests. The cool part here is that you could script out this process such that you could copy these files and send them to a shared folder. This could also be sent directly to your project's Product Owner when you do deployments. There are a lot of options here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;I hope you've enjoyed my post and learned something about Cypress. I've used it on several projects, and found it not only powerful but also fun. The tooling and community support with Cypress make it a very developer friendly tool. The support for logging and storing test run information also make it a powerful asset to any project team. I recommend checking out the docs and various guides on the &lt;a href="https://www.cypress.io/" rel="noopener noreferrer"&gt;Cypress Webisite&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks for reading my post! Follow me on &lt;a href="https://andrewevans.dev/" rel="noopener noreferrer"&gt;andrewevans.dev&lt;/a&gt; and on twitter at &lt;a href="https://twitter.com/AndrewEvans0102" rel="noopener noreferrer"&gt;@AndrewEvans0102&lt;/a&gt;. Also check out Rhythm and Binary's new Twitter feed at &lt;a href="https://twitter.com/rhythmandbinary" rel="noopener noreferrer"&gt;@rhythmandbinary&lt;/a&gt; and &lt;a href="https://www.youtube.com/channel/UCvAKKewP_o2l3XnwDzSxftw" rel="noopener noreferrer"&gt;YouTube channel&lt;/a&gt;. Thanks!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>beginners</category>
      <category>cypress</category>
      <category>react</category>
    </item>
    <item>
      <title>Celebrating Pi Day 2021</title>
      <dc:creator>Andrew Evans</dc:creator>
      <pubDate>Sun, 14 Mar 2021 12:05:53 +0000</pubDate>
      <link>https://forem.com/andrewevans0102/celebrating-pi-day-2021-420i</link>
      <guid>https://forem.com/andrewevans0102/celebrating-pi-day-2021-420i</guid>
      <description>&lt;p&gt;Every year on 03/14 everyone celebrates "Pi Day." This is a reference to the math number and also &lt;a href="https://www.raspberrypi.org/" rel="noopener noreferrer"&gt;Raspberry Pi&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I thought it'd be fun to write a short post to celebrate Pi Day. As part of my celebration, I also 3D printed a "Pi Trophy" object that I found &lt;a href="https://www.thingiverse.com/thing:1383478" rel="noopener noreferrer"&gt;on thingiverse&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fcover_image.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fcover_image.jpeg" alt="Pi Trophy" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In addition to printing this, I also used a Raspberry Pi with the &lt;a href="https://www.raspberrypi.org/products/camera-module-v2/" rel="noopener noreferrer"&gt;Camera Module&lt;/a&gt; to capture a time lapse of the print as it was happening on my 3D printer.&lt;/p&gt;

&lt;p&gt;To create the time lapse, I setup a Raspberry Pi Zero W with the &lt;a href="https://picamera.readthedocs.io/en/release-1.13/" rel="noopener noreferrer"&gt;Pi Camera&lt;/a&gt; library. If you'd like to see how this works, check out my post &lt;a href="https://rhythmandbinary.com/post/2021-02-01-creating-a-time-lapse-video-with-a-raspberry-pi" rel="noopener noreferrer"&gt;Raspberry Pi Time Lapse&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this post I just wanted to celebrate Pi Day and provide some fun background facts.&lt;/p&gt;

&lt;p&gt;I created a YouTube video of my time lapse for my 3D print:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=cVJ_dWtND9k" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl8auu8d28vcmc6vjlj7o.jpg" alt="Print" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pi Math
&lt;/h2&gt;

&lt;p&gt;The number "Pi" is the ratio of a circle's circumference to its diameter and is normally equated to about &lt;code&gt;3.14&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You see Pi in many areas of Math and Physics. The use of the Greek later &lt;code&gt;π&lt;/code&gt; representing Pi was first recorded by William Jones in 1706 (&lt;a href="https://en.wikipedia.org/wiki/Pi" rel="noopener noreferrer"&gt;source wikipedia&lt;/a&gt;). The use of Pi as a number has been recorded across the ages, even back to ancient times.&lt;/p&gt;

&lt;p&gt;The cool part about the Pi number is that it is actually the same for all circles! So this means that if you divide the circumference by the diameter for a penny its the same as a planet! Both always approximate to &lt;code&gt;3.14&lt;/code&gt;. This makes the standard number very powerful for applications in things like physics and math. The number itself has been traced across civilizations back to ancient times. If you'd like to learn more I recommend checking out the the &lt;a href="https://en.wikipedia.org/wiki/Pi" rel="noopener noreferrer"&gt;Wikipedia Page&lt;/a&gt; and &lt;a href="https://www.scientificamerican.com/article/what-is-pi-and-how-did-it-originate/" rel="noopener noreferrer"&gt;the Scientific American article&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Raspberry Pi
&lt;/h2&gt;

&lt;p&gt;Raspberry Pis are mini computers that come with a variety of connections already setup. They are great for both teaching and hobbyists. If you get models like the &lt;a href="https://www.raspberrypi.org/products/raspberry-pi-zero-w/" rel="noopener noreferrer"&gt;Raspberry Pi Zero W&lt;/a&gt;, you can connect it to wifi and bluetooth right out of the box. If you get one of the more powerful models like the new &lt;a href="https://www.raspberrypi.org/products/raspberry-pi-4-model-b/" rel="noopener noreferrer"&gt;Raspberry Pi 4 Model B&lt;/a&gt;, you get more processing power and can even connect it to two displays (mini hdmi)!&lt;/p&gt;

&lt;p&gt;The fun thing with Raspberry Pi's is that you can easily get into the world of the Internet Of Things (IOT), and connect with a great community of developers. The Raspberry Pi foundation also does a great job of providing tutorials and materials for folks new to computers. Its a great first project for anyone interested in learning programming or anything technology related. If you're looking to learn, I recommend checking out the &lt;a href="https://projects.raspberrypi.org/en/projects/raspberry-pi-getting-started" rel="noopener noreferrer"&gt;Raspberry Pi foundation tutorials and starting with small projects&lt;/a&gt;. Since the main memory that the Raspberry Pi runs on is an SD Card, it is also easy to swap out entire systems if you wanted to.&lt;/p&gt;

&lt;p&gt;I've done quite a few projects with Raspberry Pis and highly recommend them. If you'd like to see some of my Raspberry Pi projects, check out the following links.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://rhythmandbinary.com/post/2021-02-28-raspberry-pi-iot-weather-lamp" rel="noopener noreferrer"&gt;Raspberry Pi Weather Lamp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rhythmandbinary.com/post/2021-02-01-creating-a-time-lapse-video-with-a-raspberry-pi" rel="noopener noreferrer"&gt;Raspberry Pi Time Lapse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rhythmandbinary.com/post/raspberry_pi_print_server" rel="noopener noreferrer"&gt;Raspberry Pi Print Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rhythmandbinary.com/post/christmas_fun_with_a_raspberry_pi" rel="noopener noreferrer"&gt;Christmas Fun with a Raspberry Pi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rhythmandbinary.com/post/2020-07-20-building-a-weather-station-with-a-raspberry-pi-and-firebase" rel="noopener noreferrer"&gt;Building a Weather Station with a Raspberry Pi and Firebase&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;Thanks for reading my post! I hope you enjoyed the video and get a chance to celebrate "Pi Day" today. Follow me on &lt;a href="https://andrewevans.dev/" rel="noopener noreferrer"&gt;andrewevans.dev&lt;/a&gt; and on twitter at &lt;a href="https://twitter.com/AndrewEvans0102" rel="noopener noreferrer"&gt;@AndrewEvans&lt;/a&gt;. Also check out &lt;a href="https://twitter.com/RhythmAndBinary" rel="noopener noreferrer"&gt;Rhythm and Binary's new Twitter feed&lt;/a&gt; and &lt;a href="https://www.youtube.com/channel/UCvAKKewP_o2l3XnwDzSxftw" rel="noopener noreferrer"&gt;YouTube channel&lt;/a&gt;. Thanks!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Raspberry Pi Weather Lamp</title>
      <dc:creator>Andrew Evans</dc:creator>
      <pubDate>Mon, 01 Mar 2021 22:35:20 +0000</pubDate>
      <link>https://forem.com/andrewevans0102/raspberry-pi-weather-lamp-8bl</link>
      <guid>https://forem.com/andrewevans0102/raspberry-pi-weather-lamp-8bl</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-03-01-at-5.30.10-pm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-03-01-at-5.30.10-pm.png" alt="Weather Lamp" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Recently I had a fun idea for an IoT project where I could have a lamp tell me what the weather was doing. My basic premise was that the lamp would turn different colors based on conditions like Rain, Snow, Clouds, or Sun. There are many APIs that can give me forecast information freely, so I just needed to pull together a way to power the lamp and a way to change colors.&lt;/p&gt;

&lt;p&gt;In this post, I'm going to walkthrough how I built a weather lamp with a Raspberry Pi and a 3D printed lamp. To run this lamp I'm using Python and you can see a full copy of all the source code I go over at my repo &lt;a href="https://github.com/andrewevans0102/weather-lamp" rel="noopener noreferrer"&gt;https://github.com/andrewevans0102/weather-lamp&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I also created a YouTube video that covers this same project. Check it out at the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=DegG45uVvhg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw031ab6rt4szmxmfr7t9.jpg" alt="Weather Lamp Raspberry Pi Video" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Required Materials
&lt;/h2&gt;

&lt;p&gt;To do this project, you only need some basic materials that you can get either directly off of Amazon or at your local hobby store. If you wanted to 3D print the lamp itself, that would require a 3D printer. However, you can find votive lamps and covers on Amazon that provide the same look and feel and could be used instead.&lt;/p&gt;

&lt;p&gt;For the hardware part of the project:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/CanaKit-Raspberry-Wireless-Complete-Starter/dp/B072N3X39J/ref=sr_1_4?dchild=1&amp;amp;keywords=raspberry+pi+zero+w&amp;amp;qid=1614533683&amp;amp;sr=8-4" rel="noopener noreferrer"&gt;Raspberry Pi Zero W starter kit including power supply and SD card&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/Breadboards-Solderless-Breadboard-Distribution-Connecting/dp/B07DL13RZH/ref=sr_1_8?dchild=1&amp;amp;keywords=breadboard&amp;amp;qid=1614533748&amp;amp;sr=8-8" rel="noopener noreferrer"&gt;Bread Board&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/dp/B01ERP6WL4/ref=cm_sw_em_r_mt_dp_N4KN1CKWQSMCA4KQJ352" rel="noopener noreferrer"&gt;LED lights and resistors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/dp/B07GD2BWPY/ref=cm_sw_em_r_mt_dp_QDH5R89007RT4EGHH6Q0" rel="noopener noreferrer"&gt;Jumper Wires&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you have a 3D printer, you can use the STL and GCODE file that I've included in &lt;a href="https://github.com/andrewevans0102/weather-lamp" rel="noopener noreferrer"&gt;my GitHub repo in the "lamp-model" folder&lt;/a&gt;. The original file was found at &lt;a href="https://www.thingiverse.com/thing:31722" rel="noopener noreferrer"&gt;Thingiverse here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Some notes on the hardware:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There are a lot of places to get all of these things.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.amazon.com/CanaKit-Raspberry-Wireless-Complete-Starter/dp/B072N3X39J/ref=sr_1_4?dchild=1&amp;amp;keywords=raspberry+pi+zero+w&amp;amp;qid=1614533683&amp;amp;sr=8-4" rel="noopener noreferrer"&gt;CanaKit package&lt;/a&gt; I linked is great and I've used it for several projects. I highly recommend that.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Bread Board&lt;/code&gt;, &lt;code&gt;Resistors&lt;/code&gt;, &lt;code&gt;Jumper Wires&lt;/code&gt;, and &lt;code&gt;LED lights&lt;/code&gt; can be all bought together. I linked them individually just to show what you would need.&lt;/li&gt;
&lt;li&gt;The reason we need a &lt;code&gt;resistor&lt;/code&gt; is because they are necessary to control the power consumption from the Raspberry Pi. The Raspberry Pi itself can only provide about 60mA but the LEDs will want to pull more. You'll need at least a 330Ω resistor. When setting this all up, I found the &lt;a href="https://thepihut.com/blogs/raspberry-pi-tutorials/27968772-turning-on-an-led-with-your-raspberry-pis-gpio-pins" rel="noopener noreferrer"&gt;this post on PiHut&lt;/a&gt; super helpful with explaining why you need this and how it works.&lt;/li&gt;
&lt;li&gt;As I stated in the intro, if you don't have a 3D printer there are a lot of alternatives to the cool lamp cover I printed. I recommend some googling and even other DIY ideas for fun.&lt;/li&gt;
&lt;li&gt;If you notice the Raspberry Pi I'm using already has GPIO pins soldered on board. Usually if you buy a Raspberry Pi Zero W, it doesn't have these. You can solder them yourself very easily, or you could get a kit that has this all connected. You could also get a different Raspberry Pi that has the GPIO pins already onboard. If you want to go the soldering route, check out &lt;a href="https://www.amazon.com/dp/B07GJNKQ8W/ref=cm_sw_em_r_mt_dp_6ZFC02HM1CNXT7BG7P5B?_encoding=UTF8&amp;amp;psc=1" rel="noopener noreferrer"&gt;this kit on Amazon that has an iron and the necessary equipment&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting up your Raspberry Pi
&lt;/h2&gt;

&lt;p&gt;Setting up for your Raspberry Pi project only takes a few steps. If this is your first Raspberry Pi project, you should first check out &lt;a href="https://www.raspberrypi.org/" rel="noopener noreferrer"&gt;the Raspberry Pi website&lt;/a&gt; because they have some great docs on getting to know your device.&lt;/p&gt;

&lt;p&gt;For this project I'm using what is called a "headless" setup where the Raspberry Pi doesn't have a desktop environment, and is controlled via SSH and the terminal. To do this, do the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Format the SD card of your Pi with the &lt;a href="https://www.raspberrypi.org/software/" rel="noopener noreferrer"&gt;official Raspberry Pi Imager&lt;/a&gt;. Once you install the imager just choose the 32 bit Raspberry Pi Lite version that you see here:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-02-28-at-12.59.36-pm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-02-28-at-12.59.36-pm.png" alt="Raspberry Pi OS Imager" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Next, once your SD card is formatted, remove and reinsert the SD card into your computer and open the "boot" folder that opens up. This should automatically happen, but if not then just go into your Finder or File Explorer (windows) and navigate into the attached SD card and "boot" folder.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the boot folder add a &lt;code&gt;WPA_SUPPLICANT.conf&lt;/code&gt; folder that follows &lt;a href="https://www.raspberrypi.org/documentation/configuration/wireless/headless.md" rel="noopener noreferrer"&gt;the official Raspberry Pi instructions&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Additionally, you need to add an empty file that is just named "SSH" into your boot folder. This is so that when the Raspberry Pi boots up it will automatically turn on SSH, and then you can directly SSH into the machine after seeing its IP address appear on your network. If you have questions or want to know more about this, check out the Raspberry Pi docs at &lt;a href="https://www.raspberrypi.org/documentation/remote-access/ssh/README.md" rel="noopener noreferrer"&gt;https://www.raspberrypi.org/documentation/remote-access/ssh/README.md&lt;/a&gt; (look for "Enable SSH on a headless Raspberry Pi" on that webpage).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;With all of that setup, you can now go ahead and plugin your Raspberry Pi and go to your terminal to get started!&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Wiring up your Pi
&lt;/h2&gt;

&lt;p&gt;So now you've got a Raspberry Pi all connected, but you need to wire it to your LEDs so we can write some code to turn them on and off.&lt;/p&gt;

&lt;p&gt;The Raspberry Pi has &lt;a href="https://www.raspberrypi.org/documentation/usage/gpio/" rel="noopener noreferrer"&gt;GPIO headers&lt;/a&gt; which are basically several hot "leads" that you can connect wires or other peripherals for creating circuits. This is really intuitive and one of the coolest parts of the Raspberry Pi.&lt;/p&gt;

&lt;p&gt;Here is a mapping of the GPIO headers you should have on the Raspberry Pi Zero W:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fraspberry-pi-zero-w-gpio.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fraspberry-pi-zero-w-gpio.png" alt="Raspberry Pi Zero W GPIO Headers" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I copied this diagram at &lt;a href="https://raspberrypi.stackexchange.com/questions/83610/gpio-pinout-orientation-raspberypi-zero-w" rel="noopener noreferrer"&gt;stack exchange&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Borrowing this diagram from &lt;a href="https://thepihut.com/blogs/raspberry-pi-tutorials/27968772-turning-on-an-led-with-your-raspberry-pis-gpio-pins" rel="noopener noreferrer"&gt;the PiHut's post on LED lights&lt;/a&gt;. Your first setup will look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-02-28-at-1.10.09-pm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2021-02-28-at-1.10.09-pm.png" alt="Raspberry Pi Connected to LEDs" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;this image was copied from &lt;a href="https://thepihut.com/blogs/raspberry-pi-tutorials/27968772-turning-on-an-led-with-your-raspberry-pis-gpio-pins" rel="noopener noreferrer"&gt;the PiHut post here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you notice, the Raspberry Pi in the picture is a Raspberry Pi 4 (or at least some variation of it). That's fine for our purpose because it still has the same setup for a Raspberry Pi Zero W.&lt;/p&gt;

&lt;p&gt;To map the connections on your Raspberry Pi Zero W, I recommend following the above picture and you can use &lt;a href="https://pinout.xyz/pinout/3v3_power" rel="noopener noreferrer"&gt;the diagram available on pinout for a more interactive view&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What you see here is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;first a &lt;code&gt;ground connection&lt;/code&gt; from the a ground pin on the Raspberry Pi to the breadboard&lt;/li&gt;
&lt;li&gt;a &lt;code&gt;power connection&lt;/code&gt; from one of the Raspberry Pi GPIO power pins to the breadboard&lt;/li&gt;
&lt;li&gt;a &lt;code&gt;resistor&lt;/code&gt; that runs between the breadboard connection for the LED light and the Raspberry Pi power source (reduces the energy pull from the Raspberry Pi)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you're new to breadboards, they function as basically mediums that carry current. You should not two important concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The two lines on the edge of the breadboard (denoted with the blue and red lines) are considered "rails". Anything you plugin to one of the pins on these "rails" is accessible to anything that connects to the rail. This is super useful if you want to have a ground connection for an array of circuits.&lt;/li&gt;
&lt;li&gt;The vertical lines (that should have numbers next to them) also share a current. So basically because the resistor is plugged into the breadboard between the LED light and the grounded rail, the current is shared along that line in such a way that if you were to move the resistor connection a little farther down the same vertical line you could have a second LED draw power on this line.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If my attempt at explaining breadboards here is confusing, please check out the PiHuts post which also provides a similar (and probably a better) explanation at &lt;a href="https://thepihut.com/blogs/raspberry-pi-tutorials/27968772-turning-on-an-led-with-your-raspberry-pis-gpio-pins" rel="noopener noreferrer"&gt;this website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On my Raspberry Pi Zero W, for my first setup I made the following connections:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Jumper Wire from &lt;code&gt;GND pin on Raspberry Pi (next to pin GPIO 26)&lt;/code&gt; to &lt;code&gt;blue "rail" on breadboard&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Resistor from &lt;code&gt;blue rail&lt;/code&gt; to &lt;code&gt;vertical column on bread board (mine had a #2 next to it)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;LED with short leg (no kink)&lt;/code&gt; to `vertical column on bread board (mine had a #2 next to it)&lt;/li&gt;
&lt;li&gt;Jumper Wire from &lt;code&gt;GPIO pin 21 on Raspberry Pi&lt;/code&gt; to &lt;code&gt;vertical column on bread board where LED long leg (no kink) was connected (mine had a #3 next to it)&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So now you should have your Raspberry Pi wired up, so now we can write code to turn on the light!&lt;/p&gt;

&lt;h2&gt;
  
  
  First Program
&lt;/h2&gt;

&lt;p&gt;So at this point you should have your Raspberry Pi wired up, and you can go ahead and turn it on. You should look at your router or whatever program you use to see connected IP addresses. Find your Raspberry Pi's IP address and then go to your computers terminal. You can SSH into your pi with &lt;code&gt;ssh pi@&amp;lt;ip_address&amp;gt;&lt;/code&gt;. Then you should go ahead and run &lt;code&gt;sudo raspi-config&lt;/code&gt; to change your SSH password, set your timezone, and (if you wanted to) change your hostname. I changed mine to "weatherlight" so I could just ssh into it with &lt;code&gt;ssh pi@weatherlight.local&lt;/code&gt;. If you have more questions about raspi-config, check out the raspberry pi docs at &lt;a href="https://www.raspberrypi.org/documentation/configuration/raspi-config.md" rel="noopener noreferrer"&gt;https://www.raspberrypi.org/documentation/configuration/raspi-config.md&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now we're going to be using Python's &lt;code&gt;RPI.GPIO&lt;/code&gt; library, check out their page on how to install it at &lt;a href="https://pypi.org/project/RPi.GPIO/" rel="noopener noreferrer"&gt;https://pypi.org/project/RPi.GPIO/&lt;/a&gt;. You should be able to use &lt;code&gt;pip3&lt;/code&gt; but if that's an issue, install that package with the instructions at &lt;a href="https://www.raspberrypi.org/documentation/linux/software/python.md" rel="noopener noreferrer"&gt;https://www.raspberrypi.org/documentation/linux/software/python.md&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you've go the &lt;code&gt;RPI.GPIO&lt;/code&gt; library setup, you can run your first program. So When I was just starting out, I followed the &lt;a href="https://thepihut.com/blogs/raspberry-pi-tutorials/27968772-turning-on-an-led-with-your-raspberry-pis-gpio-pins" rel="noopener noreferrer"&gt;the PiHut post here&lt;/a&gt;. Copying basically the same program they provide there, my first program was this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`py&lt;br&gt;
import RPi.GPIO as GPIO&lt;br&gt;
import time&lt;/p&gt;

&lt;p&gt;white_light=21&lt;/p&gt;

&lt;p&gt;GPIO.setmode(GPIO.BCM)&lt;br&gt;
GPIO.setwarnings(False)&lt;br&gt;
GPIO.setup(white_light,GPIO.OUT)&lt;br&gt;
print("LED on")&lt;br&gt;
GPIO.output(white_light,GPIO.HIGH)&lt;br&gt;
time.sleep(1)&lt;br&gt;
print("LED off")&lt;br&gt;
GPIO.output(white_light,GPIO.LOW)&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you're following along &lt;a href="https://github.com/andrewevans0102/weather-lamp" rel="noopener noreferrer"&gt;in my GitHub repo&lt;/a&gt; this is the program &lt;code&gt;first_light.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;All this does is pass the command to turn ON the LED light, wait 1 second, and then turn it off. If you run this, you should see your light turn on, and then off.&lt;/p&gt;

&lt;p&gt;For this project, I also needed to connect multiple lights, so I followed the same process and connected several LEDs to my Raspberry Pi. I then extended the &lt;code&gt;first_light.py&lt;/code&gt; program to include these connections to verify they were running correctly with:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`&lt;br&gt;
import RPi.GPIO as GPIO&lt;br&gt;
import time&lt;/p&gt;

&lt;p&gt;white_light=21&lt;br&gt;
green_light=20&lt;br&gt;
yellow_light=16&lt;br&gt;
blue_light=14&lt;br&gt;
red_light=18&lt;br&gt;
red_2=17&lt;br&gt;
blue_2=27&lt;br&gt;
yellow_2=22&lt;br&gt;
green_2=10&lt;br&gt;
white_2=9&lt;/p&gt;

&lt;p&gt;GPIO.setmode(GPIO.BCM)&lt;br&gt;
GPIO.setwarnings(False)&lt;br&gt;
GPIO.setup(white_light,GPIO.OUT)&lt;br&gt;
print("LED on")&lt;br&gt;
GPIO.output(white_light,GPIO.HIGH)&lt;br&gt;
time.sleep(1)&lt;br&gt;
print("LED off")&lt;br&gt;
GPIO.output(white_light,GPIO.LOW)&lt;/p&gt;

&lt;p&gt;GPIO.setmode(GPIO.BCM)&lt;br&gt;
GPIO.setwarnings(False)&lt;br&gt;
GPIO.setup(green_light,GPIO.OUT)&lt;br&gt;
print("LED on")&lt;br&gt;
GPIO.output(green_light,GPIO.HIGH)&lt;br&gt;
time.sleep(1)&lt;br&gt;
print("LED off")&lt;br&gt;
GPIO.output(green_light,GPIO.LOW)&lt;/p&gt;

&lt;p&gt;GPIO.setmode(GPIO.BCM)&lt;br&gt;
GPIO.setwarnings(False)&lt;br&gt;
GPIO.setup(yellow_light,GPIO.OUT)&lt;br&gt;
print("LED on")&lt;br&gt;
GPIO.output(yellow_light,GPIO.HIGH)&lt;br&gt;
time.sleep(1)&lt;br&gt;
print("LED off")&lt;br&gt;
GPIO.output(yellow_light,GPIO.LOW)&lt;/p&gt;

&lt;p&gt;GPIO.setmode(GPIO.BCM)&lt;br&gt;
GPIO.setwarnings(False)&lt;br&gt;
GPIO.setup(blue_light,GPIO.OUT)&lt;br&gt;
print("LED on")&lt;br&gt;
GPIO.output(blue_light,GPIO.HIGH)&lt;br&gt;
time.sleep(1)&lt;br&gt;
print("LED off")&lt;br&gt;
GPIO.output(blue_light,GPIO.LOW)&lt;/p&gt;

&lt;p&gt;GPIO.setmode(GPIO.BCM)&lt;br&gt;
GPIO.setwarnings(False)&lt;br&gt;
GPIO.setup(red_light,GPIO.OUT)&lt;br&gt;
print("LED on")&lt;br&gt;
GPIO.output(red_light,GPIO.HIGH)&lt;br&gt;
time.sleep(1)&lt;br&gt;
print("LED off")&lt;br&gt;
GPIO.output(red_light,GPIO.LOW)&lt;/p&gt;

&lt;p&gt;GPIO.setmode(GPIO.BCM)&lt;br&gt;
GPIO.setwarnings(False)&lt;br&gt;
GPIO.setup(red_2,GPIO.OUT)&lt;br&gt;
print("LED on")&lt;br&gt;
GPIO.output(red_2,GPIO.HIGH)&lt;br&gt;
time.sleep(1)&lt;br&gt;
print("LED off")&lt;br&gt;
GPIO.output(red_2,GPIO.LOW)&lt;/p&gt;

&lt;p&gt;GPIO.setmode(GPIO.BCM)&lt;br&gt;
GPIO.setwarnings(False)&lt;br&gt;
GPIO.setup(blue_2,GPIO.OUT)&lt;br&gt;
print("LED on")&lt;br&gt;
GPIO.output(blue_2,GPIO.HIGH)&lt;br&gt;
time.sleep(1)&lt;br&gt;
print("LED off")&lt;br&gt;
GPIO.output(blue_2,GPIO.LOW)&lt;/p&gt;

&lt;p&gt;GPIO.setmode(GPIO.BCM)&lt;br&gt;
GPIO.setwarnings(False)&lt;br&gt;
GPIO.setup(yellow_2,GPIO.OUT)&lt;br&gt;
print("LED on")&lt;br&gt;
GPIO.output(yellow_2,GPIO.HIGH)&lt;br&gt;
time.sleep(1)&lt;br&gt;
print("LED off")&lt;br&gt;
GPIO.output(yellow_2,GPIO.LOW)&lt;/p&gt;

&lt;p&gt;GPIO.setmode(GPIO.BCM)&lt;br&gt;
GPIO.setwarnings(False)&lt;br&gt;
GPIO.setup(green_2,GPIO.OUT)&lt;br&gt;
print("LED on")&lt;br&gt;
GPIO.output(green_2,GPIO.HIGH)&lt;br&gt;
time.sleep(1)&lt;br&gt;
print("LED off")&lt;br&gt;
GPIO.output(green_2,GPIO.LOW)&lt;/p&gt;

&lt;p&gt;GPIO.setmode(GPIO.BCM)&lt;br&gt;
GPIO.setwarnings(False)&lt;br&gt;
GPIO.setup(white_2,GPIO.OUT)&lt;br&gt;
print("LED on")&lt;br&gt;
GPIO.output(white_2,GPIO.HIGH)&lt;br&gt;
time.sleep(1)&lt;br&gt;
print("LED off")&lt;br&gt;
GPIO.output(white_2,GPIO.LOW)&lt;/p&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you're following along &lt;a href="https://github.com/andrewevans0102/weather-lamp" rel="noopener noreferrer"&gt;in my GitHub repo&lt;/a&gt; this is the program &lt;code&gt;verify_lights.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So now I had everything wired and could see everything was working with my program. I now needed to pull in a weather forecast and adjust the lights accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Weather Forecast
&lt;/h2&gt;

&lt;p&gt;So in order to get the weather, there are many APIs that provide variations of weather information. I live in the US and decided to go with the (free) NOAA APIs that the National Weather Service provides. In order to use those APIs, you just need a Latitude and Longitude to be able to pass and get the current conditions. I'm not going to go into the ways to use their APIs, but will refer you to my previous post on how to find the weather forecast for your area at &lt;a href="https://rhythmandbinary.com/post/National_Weather_Service_API" rel="noopener noreferrer"&gt;https://rhythmandbinary.com/post/National_Weather_Service_API&lt;/a&gt;. I specifically was looking at the "hourly" forecast endpoint, so I could get the conditions each hour and update my light accordingly.&lt;/p&gt;

&lt;p&gt;The URL I ended up getting for my location (Richmond, Va) is &lt;a href="https://api.weather.gov/gridpoints/AKQ/38,80/forecast/hourly" rel="noopener noreferrer"&gt;https://api.weather.gov/gridpoints/AKQ/38,80/forecast/hourly&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you've got the URL for your location, you can now write some Python to call the weather service and pull out the necessary information.&lt;/p&gt;

&lt;p&gt;I used &lt;a href="https://pypi.org/project/requests/" rel="noopener noreferrer"&gt;Python Requests&lt;/a&gt; to make my API call and the basic call and data parsing looked like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`&lt;/p&gt;

&lt;h1&gt;
  
  
  weather call
&lt;/h1&gt;

&lt;p&gt;weatherURL = "&lt;a href="https://api.weather.gov/gridpoints/AKQ/38,80/forecast/hourly" rel="noopener noreferrer"&gt;https://api.weather.gov/gridpoints/AKQ/38,80/forecast/hourly&lt;/a&gt;"&lt;/p&gt;

&lt;h1&gt;
  
  
  call weather service
&lt;/h1&gt;

&lt;p&gt;response = requests.get(weatherURL)&lt;/p&gt;

&lt;h1&gt;
  
  
  parse JSON
&lt;/h1&gt;

&lt;p&gt;hourlyWeather = response.json()&lt;br&gt;
properties = hourlyWeather["properties"]&lt;br&gt;
periods = properties["periods"]&lt;br&gt;
rightNow = periods[0]&lt;br&gt;
weatherCondition = rightNow["shortForecast"]&lt;/p&gt;

&lt;h1&gt;
  
  
  log weather condition that was just called
&lt;/h1&gt;

&lt;p&gt;print(weatherCondition)&lt;/p&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now I had my weather forecast and current conditions, all I had to do was make a little conditional statement to take in the values and turn on the corresponding light.&lt;/p&gt;

&lt;p&gt;I created an array of the lights I had connected, and then added the conditional to "turn on" the appropriate light:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`&lt;/p&gt;

&lt;h1&gt;
  
  
  weather call
&lt;/h1&gt;

&lt;p&gt;weatherURL = "&lt;a href="https://api.weather.gov/gridpoints/AKQ/38,80/forecast/hourly" rel="noopener noreferrer"&gt;https://api.weather.gov/gridpoints/AKQ/38,80/forecast/hourly&lt;/a&gt;"&lt;/p&gt;

&lt;h1&gt;
  
  
  call weather service
&lt;/h1&gt;

&lt;p&gt;response = requests.get(weatherURL)&lt;/p&gt;

&lt;h1&gt;
  
  
  parse JSON
&lt;/h1&gt;

&lt;p&gt;hourlyWeather = response.json()&lt;br&gt;
properties = hourlyWeather["properties"]&lt;br&gt;
periods = properties["periods"]&lt;br&gt;
rightNow = periods[0]&lt;br&gt;
weatherCondition = rightNow["shortForecast"]&lt;/p&gt;

&lt;h1&gt;
  
  
  log weather condition that was just called
&lt;/h1&gt;

&lt;p&gt;print(weatherCondition)&lt;/p&gt;

&lt;h1&gt;
  
  
  figure out which light to turn on
&lt;/h1&gt;

&lt;p&gt;if (weatherCondition.find('Sunny')):&lt;br&gt;
    # yellow&lt;br&gt;
    turnOn = light_colors[0]&lt;br&gt;
elif (weatherCondition.find('Clear')):&lt;br&gt;
    # white&lt;br&gt;
    turnOn = light_colors[1]&lt;br&gt;
elif (weatherCondition.find('Cloudy')):&lt;br&gt;
    # green&lt;br&gt;
    turnOn = light_colors[2]&lt;br&gt;
elif (weatherCondition.find('Showers')):&lt;br&gt;
    # blue&lt;br&gt;
    turnOn = light_colors[3]&lt;/p&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I had that working, and so now I could connect this to my wired up Raspberry Pi. If you're following along &lt;a href="https://github.com/andrewevans0102/weather-lamp" rel="noopener noreferrer"&gt;in my GitHub repo&lt;/a&gt; this is the program &lt;code&gt;local_weather.py&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting it all Together
&lt;/h2&gt;

&lt;p&gt;So now I had my wired up Raspberry Pi, my weather forecast, and now I just needed to (in code) connect the two.&lt;/p&gt;

&lt;p&gt;At this point I also wanted to add some logging, and error handling since the Weather Service API's occasionally have issues.&lt;/p&gt;

&lt;p&gt;I also wanted a way to see if the project was working with some kind of visual indicator. That way I understood if there was no light shown in my little lamp.&lt;/p&gt;

&lt;p&gt;All of this ended up with the following:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`&lt;br&gt;
import RPi.GPIO as GPIO&lt;br&gt;
import time&lt;br&gt;
import logging&lt;br&gt;
import requests&lt;/p&gt;

&lt;h1&gt;
  
  
  definition of light object
&lt;/h1&gt;

&lt;p&gt;class light:&lt;br&gt;&lt;br&gt;
    def &lt;strong&gt;init&lt;/strong&gt; (self, name, pin):&lt;br&gt;&lt;br&gt;
        self.name = name&lt;br&gt;
        self.pin = pin &lt;/p&gt;

&lt;h1&gt;
  
  
  create a list of lights
&lt;/h1&gt;

&lt;p&gt;lights = []&lt;/p&gt;

&lt;h1&gt;
  
  
  this covers two LEDs
&lt;/h1&gt;

&lt;p&gt;lights.append( light("blue", 27))&lt;/p&gt;

&lt;h1&gt;
  
  
  this covers two LEDs
&lt;/h1&gt;

&lt;p&gt;lights.append( light("white", 14))&lt;/p&gt;

&lt;h1&gt;
  
  
  this covers two LEDs
&lt;/h1&gt;

&lt;p&gt;lights.append( light("yellow", 16))&lt;/p&gt;

&lt;h1&gt;
  
  
  green lights for sucess indicator
&lt;/h1&gt;

&lt;p&gt;lights.append( light("green_1", 20))&lt;br&gt;
lights.append( light("green_2", 10))&lt;/p&gt;

&lt;h1&gt;
  
  
  red lights for error indicator
&lt;/h1&gt;

&lt;p&gt;lights.append( light("red_1", 18))&lt;br&gt;
lights.append( light("red_2", 17))&lt;/p&gt;

&lt;h1&gt;
  
  
  logging
&lt;/h1&gt;

&lt;p&gt;logging.basicConfig(filename='/home/pi/weather-lamp/history_daily.log',format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p', level=logging.INFO)&lt;/p&gt;

&lt;h1&gt;
  
  
  LED colors
&lt;/h1&gt;

&lt;p&gt;light_colors = ['yellow', 'white', 'blue', 'green', 'red']&lt;/p&gt;

&lt;h1&gt;
  
  
  weather call
&lt;/h1&gt;

&lt;p&gt;weatherURL = "&lt;a href="https://api.weather.gov/gridpoints/AKQ/38,80/forecast/hourly" rel="noopener noreferrer"&gt;https://api.weather.gov/gridpoints/AKQ/38,80/forecast/hourly&lt;/a&gt;"&lt;/p&gt;

&lt;h1&gt;
  
  
  figure out which light to turn on
&lt;/h1&gt;

&lt;p&gt;turnOn = ''&lt;/p&gt;

&lt;p&gt;def turnOffAllLights():&lt;br&gt;
    # loop through the list and turn em all off&lt;br&gt;
    for singleLED in lights:&lt;br&gt;
        logging.info("turning off " + singleLED.name)&lt;br&gt;
        GPIO.output(singleLED.pin, GPIO.LOW)&lt;/p&gt;

&lt;p&gt;def turnOnGPIOLight( lightName ):&lt;br&gt;
    logging.info("looking for light name " + lightName)&lt;br&gt;
    for singleLED in lights:&lt;br&gt;
        if(singleLED.name.find(lightName) != -1):&lt;br&gt;
            logging.info("turning on " + singleLED.name)&lt;br&gt;
            GPIO.output(singleLED.pin, GPIO.HIGH)&lt;br&gt;
    return&lt;/p&gt;

&lt;p&gt;try:&lt;br&gt;
    # official start of program&lt;br&gt;
    logging.info("starting program")&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# turn on GPIO settings so we can work with the LEDs connected
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

# call setup to initialize all the lights
for singleLED in lights:
    GPIO.setup(singleLED.pin, GPIO.OUT)

# first turn off all the lights
logging.info("lets first turn all the lights off")
turnOffAllLights()

# call weather service
logging.info("now lets call the weather service")
response = requests.get(weatherURL)
logging.info("weather service API was called with a return status code of " + str(response.status_code))

# parse JSON
logging.info("parsing JSON")
hourlyWeather = response.json()
properties = hourlyWeather["properties"]
periods = properties["periods"]
rightNow = periods[0]
weatherCondition = rightNow["shortForecast"]

# log weather condition that was just called
logging.info("weather condition that was found is " + weatherCondition)

# figure out which light to turn on
if (weatherCondition.find('Sunny') != -1) or (weatherCondition.find('Clear') != -1):
    # yellow
    turnOn = light_colors[0]
elif (weatherCondition.find('Cloudy') != -1):
    # white
    turnOn = light_colors[1]
elif (weatherCondition.find('Rain') != -1) or (weatherCondition.find('Snow') != -1):
    # blue
    turnOn = light_colors[2]

# turn on green light to indicate success
turnOnGPIOLight(light_colors[3])

# log the output for successful run
logging.info("color to light has been found to be " + turnOn)
turnOnGPIOLight(turnOn)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;except Exception as error:&lt;br&gt;
    # when error occurs in call, show red light so we know an issue happend&lt;br&gt;
    turnOnGPIOLight(light_colors[4])&lt;br&gt;
    logging.info('exception occured')&lt;br&gt;
    logging.info(error)&lt;/p&gt;

&lt;p&gt;logging.info("program finished")&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you're following along &lt;a href="https://github.com/andrewevans0102/weather-lamp" rel="noopener noreferrer"&gt;in my GitHub repo&lt;/a&gt; this is the program &lt;code&gt;weather_light.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I also rewired my original breadboard to have a shared connection for the LEDs so two LEDs could share the same power draw from the PI. This looks like the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Flamp_wiring.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Flamp_wiring.jpg" alt="Weather Lamp" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I had a little wooden box that I had used previously to hide wires, it has some holes cut in the back of it where I can run jumper cables out of. So basically I put my Raspberry Pi and a breadboard in the wooden box. Then I ran a second set of Jumper cables out of the box to a second breadboard that I covered with my lamp.&lt;/p&gt;

&lt;p&gt;The end product looked like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Ffinished_2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Ffinished_2.jpg" alt="Weather Lamp Connected to Raspberry Pi" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The wires and the Raspberry Pi were primarily in the box, and I ran some extra jumper cables out the back to connect to the breadboard under the lamp. The end product hid most of the wires and had a nice finished look in our kitchen like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Ffinished_1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Ffinished_1.jpg" alt="Weather Lamp and Raspberry Pi on shelf" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Automating It
&lt;/h2&gt;

&lt;p&gt;So for the last step, I wanted this to run every hour on the hour. To do this I just used a cronjob that runs on the Raspberry Pi. You can do this if you open your terminal (in your SSHed session) and run &lt;code&gt;crontab -e&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I also wanted to make sure my log file doesn't get too big. Since the Raspberry Pi has limited file capacity, its important to keep track of how much memory is being used. To that end I wrote a small python program that deletes the logfile I write to when the program runs every hour:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`&lt;br&gt;
import os &lt;br&gt;
import logging&lt;/p&gt;

&lt;h1&gt;
  
  
  logging
&lt;/h1&gt;

&lt;p&gt;logging.basicConfig(filename='/home/pi/weather-lamp/history_delete.log',format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p', level=logging.INFO)&lt;/p&gt;

&lt;p&gt;logging.info("program started")&lt;/p&gt;

&lt;p&gt;filename="/home/pi/weather-lamp/history_daily.log"&lt;/p&gt;

&lt;p&gt;try: &lt;br&gt;
    logging.info("starting the removal of the file") &lt;br&gt;
    os.remove(filename)&lt;br&gt;
    logging.info("file has been deleted successfully")&lt;br&gt;
except Exception as error:&lt;br&gt;
    logging.info('exception occured')&lt;br&gt;
    logging.info(error)&lt;/p&gt;

&lt;p&gt;logging.info("program finished")&lt;/p&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you're following along &lt;a href="https://github.com/andrewevans0102/weather-lamp" rel="noopener noreferrer"&gt;in my GitHub repo&lt;/a&gt; this is the program &lt;code&gt;cleanup_files.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The end product that I had with both the hourly run of my program running and the cleanup program in crontab looks like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`&lt;br&gt;
0 * * * * python3 /home/pi/weather-lamp/weather_light.py&lt;br&gt;
30 15 * * * python3 /home/pi/weather-lamp/cleanup_files.py&lt;/p&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;So I hope you've enjoyed this post and learned something in the process. This project was a lot of fun and has been a fun talking piece in our kitchen. The light also looks really cool at night as the LED and the white fillament make it look like a small votive lamp. I highly recommend trying this project out, and looking &lt;a href="(https://github.com/andrewevans0102/weather-lamp)"&gt;at my GitHub repo&lt;/a&gt;. I also recommend looking at other cool things you can do with GPIO headers and projects on the Raspberry Pi.&lt;/p&gt;

&lt;p&gt;Thanks for reading my post! Follow me on &lt;a href="https://www.andrewevans.dev" rel="noopener noreferrer"&gt;andrewevans.dev&lt;/a&gt; and on twitter at &lt;a href="https://www.twitter.com/andrewevans0102" rel="noopener noreferrer"&gt;@AndrewEvans0102&lt;/a&gt;. Also check out Rhythm and Binary's new Twitter feed at &lt;a href="https://twitter.com/rhythmandbinary" rel="noopener noreferrer"&gt;@rhythmandbinary&lt;/a&gt; and &lt;a href="https://www.youtube.com/channel/UCvAKKewP_o2l3XnwDzSxftw" rel="noopener noreferrer"&gt;YouTube channel&lt;/a&gt;. Thanks!&lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>iot</category>
      <category>python</category>
      <category>3dprinting</category>
    </item>
    <item>
      <title>Making NodeJS Functions Sleep</title>
      <dc:creator>Andrew Evans</dc:creator>
      <pubDate>Wed, 10 Feb 2021 15:41:30 +0000</pubDate>
      <link>https://forem.com/andrewevans0102/making-nodejs-functions-sleep-43en</link>
      <guid>https://forem.com/andrewevans0102/making-nodejs-functions-sleep-43en</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fcover.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fcover.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Here are my two cats Chestnut and Rey. They're showing what a well rested NodeJS function looks like.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When learning JavaScript for the first time, one of the biggest challenges is understanding the event loop. Unlike many other languages, JavaScript operates inside a hosted environment which determines how the code is actually ran. This means that things like scope and order can make or break JavaScript programs. It makes it harder when you want to streamline processing to force control on things like threads or API calls.&lt;/p&gt;

&lt;p&gt;One common approach to handling things like this is using &lt;code&gt;async await&lt;/code&gt;. Which forces things that are async (like promises) to complete before getting a payload and making calls. I actually cover a basic walkthrough of this in my post &lt;a href="https://rhythmandbinary.com/post/Optimizing_Angular_with_Async_Await" rel="noopener noreferrer"&gt;Optimizing Angular with Async Await&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Recently, I was working on an NodeJS API that calls a set of endpoints to provide a weather forecast. I'm using some of the NOAA Endpoints that you can see &lt;a href="https://www.weather.gov/documentation/services-web-api" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The issue I had was that one of the API calls would intermittently fail. What I noticed in testing was that if it failed, but I waited a few seconds and tried again, it worked. This was pretty frustrating because it would happen at different points in the day. My assumption was this was based on load etc. on the NOAA APIs. Regardless, I needed to build in some mechanism to handle this behavior. Otherwise, it would force my API to fail as a result.&lt;/p&gt;

&lt;p&gt;So to do this I built a custom "sleep" function. There are several ways to do this (and npm packages as well). I liked my function because it was very simple and I understood what it was doing end to end.&lt;/p&gt;

&lt;p&gt;In this post I'm just going to show what I did, and how you can use this in your programs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lets start with the code
&lt;/h2&gt;

&lt;p&gt;So to start off with I originally had an API call that looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    const NOAAWeeklyForecast = await axios.get(endpoint);
    if (NOAAWeeklyForecast === undefined) {
        throw new Error('error when calling NOAA API for weekly forecast');
    }

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

&lt;/div&gt;



&lt;p&gt;I'm using &lt;a href="https://www.npmjs.com/package/axios" rel="noopener noreferrer"&gt;axios&lt;/a&gt; to handle my AJAX calls here.&lt;/p&gt;

&lt;p&gt;This was pretty simple, except (as i stated earlier) if an error happened, the rest of my endpoint would fail.&lt;/p&gt;

&lt;p&gt;So to do this, I first created a "sleep" method that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// wrap settimeout in a promise to create a wait
const sleep = (ms) =&amp;gt; {
    return new Promise((resolve) =&amp;gt; setTimeout(resolve, ms));
};

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

&lt;/div&gt;



&lt;p&gt;As the comment suggests, it wraps setTimeout with a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise" rel="noopener noreferrer"&gt;promise&lt;/a&gt; and using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout" rel="noopener noreferrer"&gt;setTimeout&lt;/a&gt; to delay activity. So if you call this with &lt;code&gt;async await&lt;/code&gt; then it will pause or "sleep" any function that calls it.&lt;/p&gt;

&lt;p&gt;I refactored my original axios call to have its own method and it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const callNOAA = async (endpoint) =&amp;gt; {
    let attempts = 5;
    let response = undefined;
    while (attempts &amp;gt; 0) {
        try {
            response = await axios.get(endpoint);
            break;
        } catch (error) {
            console.log(
                `error occured with message ${error} and attempts ${attempts.toString()}`
            );
            attempts = attempts - 1;
            console.log('waiting 5 seconds and then am going to try again');
            // wait 5 seconds before running again
            await sleep(5000);
        }
    }
    return response;
};

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

&lt;/div&gt;



&lt;p&gt;If you notice it calls my &lt;code&gt;sleep&lt;/code&gt; function, and basically retries the HTTP call in 5 second intervals. Each time keeping track of a number of attempts. If it fails after 5 attempts, then it throws an exception (which can be handled by any function that calls this).&lt;/p&gt;

&lt;h2&gt;
  
  
  Axios Interceptors
&lt;/h2&gt;

&lt;p&gt;So after adding this, I've been watching my APIs performance for some time. This basically resolved the intermittent failures that I saw originally.&lt;/p&gt;

&lt;p&gt;I could have just as easily used something like &lt;a href="https://www.npmjs.com/package/axios-retry" rel="noopener noreferrer"&gt;axios-retry&lt;/a&gt; or several other packages that do something similar. However, I liked this implementation because it used some core JavaScript concepts and there was no "black box" that you often get with packages.&lt;/p&gt;

&lt;p&gt;Another thing that I wanted to mention was that if you're using axios then there are also things called &lt;code&gt;interceptors&lt;/code&gt;. They can be utilized with this kind of behavior, but also can be a great way to track HTTP calls in your NodeJS APIs.&lt;/p&gt;

&lt;p&gt;I used axios &lt;code&gt;request&lt;/code&gt; and &lt;code&gt;response&lt;/code&gt; interceptors like you see here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// request interceptor
axios.interceptors.request.use(
    function (config) {
        console.log(`request sent with config ${JSON.stringify(config)}`);
        return config;
    },
    function (error) {
        console.log(`request sent with error ${error}`);
        return Promise.reject(error);
    }
);

// response interceptor
axios.interceptors.response.use(
    function (response) {
        console.log(`response sent with return of ${response.status}`);
        return response;
    },
    function (error) {
        console.log(`response sent with ${error}`);
        return Promise.reject(error);
    }
);

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

&lt;/div&gt;



&lt;p&gt;If you'd like to learn more about axios interceptors, please check out their &lt;a href="https://github.com/axios/axios#interceptors" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;I hope you enjoyed this post and also learned about a way to make your NodeJS functions sleep. I did quite a bit of googling leading up to this and found that there is a lot of documentation on this method as well as others to make your NodeJS functions sleep.&lt;/p&gt;

&lt;p&gt;Thanks for reading my post! Follow me on &lt;a href="https://www.andrewevans.dev" rel="noopener noreferrer"&gt;andrewevans.dev&lt;/a&gt; and on twitter at &lt;a href="https://www.twitter.com/AndrewEvans0102" rel="noopener noreferrer"&gt;@AndrewEvans&lt;/a&gt;. Also check out Rhythm and Binary's new Twitter feed at &lt;a href="https://twitter.com/rhythmandbinary" rel="noopener noreferrer"&gt;@rhythmandbinary&lt;/a&gt; and &lt;a href="https://www.youtube.com/channel/UCvAKKewP_o2l3XnwDzSxftw" rel="noopener noreferrer"&gt;YouTube channel&lt;/a&gt;. Thanks!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>Connecting Microsoft SignalR with Angular</title>
      <dc:creator>Andrew Evans</dc:creator>
      <pubDate>Fri, 16 Oct 2020 20:25:54 +0000</pubDate>
      <link>https://forem.com/andrewevans0102/connecting-microsoft-signalr-with-angular-147g</link>
      <guid>https://forem.com/andrewevans0102/connecting-microsoft-signalr-with-angular-147g</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fstar-wars.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fstar-wars.jpg" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Rey and Kylo Ren use SignalR to communicate with the force. Image was orginally copied from &lt;a href="https://www.techradar.com/news/star-wars-2020" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Microsoft's SignalR is a very powerful technology that enables websocket connections between clients. The technology has been around for quite some time, but now with Azure its even easier to get started.&lt;/p&gt;

&lt;p&gt;Microsoft's &lt;a href="https://www.npmjs.com/package/@aspnet/signalr" rel="noopener noreferrer"&gt;@aspnet/signalr&lt;/a&gt; package makes it possible to leverage both Azure and Angular to create applications that utilize SignalR. In this post, I'm going to walkthrough setting it up in a chat application and discuss the technology along the way. If you want to follow along, please check out &lt;a href="https://github.com/andrewevans0102/azure-signalr2" rel="noopener noreferrer"&gt;my sample application GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the SignalR Service?
&lt;/h2&gt;

&lt;p&gt;SignalR is a technology from Microsoft that provides real time communication between client and server via &lt;a href="https://en.wikipedia.org/wiki/WebSocket" rel="noopener noreferrer"&gt;WebSockets&lt;/a&gt;. You most often see it used for high frequency applications like chat applications, gaming, dashboards, anything that requires real time updates.&lt;/p&gt;

&lt;p&gt;SignalR is offered as either a hosted technology, or you can leverage Azure to use their SignalR service. The Azure SignalR service is very easy to get started with and supports web clients, mobile apps, servers, and IoT devices.&lt;/p&gt;

&lt;p&gt;SignalR works for both large and small applications. If you use the SignalR service that Azure provides, you get all of this technology without having to manage the underlying infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-10-16-at-4.36.21-pm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-10-16-at-4.36.21-pm.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;image was originally copied from &lt;a href="https://docs.microsoft.com/en-us/azure/azure-signalr/signalr-overview" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Azure's SignalR service provides broad language support to include SDKs for ASP.NET Core, ASP.NET C#, and JavaScript. You can also leverage serverless with Azure Functions to connect to the SignalR service, and also to handle the message negotiation.&lt;/p&gt;

&lt;p&gt;SignalR can also deliver messages generically as they are sent to a hub, or it can send messages directly to specific clients.&lt;/p&gt;

&lt;p&gt;If you'd like to see more on the SignalR technology, I recommend checking out the &lt;a href="https://docs.microsoft.com/en-us/azure/azure-signalr/signalr-overview" rel="noopener noreferrer"&gt;Microsoft page on it here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works with Angular
&lt;/h2&gt;

&lt;p&gt;So with SignalR you can connect webclients to send and receive messages. It's actually pretty easy to setup with the &lt;a href="https://www.npmjs.com/package/@aspnet/signalr" rel="noopener noreferrer"&gt;@aspnet/signalr&lt;/a&gt; package and a custom service.&lt;/p&gt;

&lt;p&gt;The basic setup looks like the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-10-16-at-1.11.21-pm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-10-16-at-1.11.21-pm.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the left you see the connection process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The client calls a negotiate function to setup the WebSocket connection between the client and the SignalR service.&lt;/li&gt;
&lt;li&gt;The SignalR service connects and creates a connection through a second function that the webclient listens to&lt;/li&gt;
&lt;li&gt;The webclient sends messages through the messages function which are then propagated to any other webclients connected to SignalR&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On the right you see the end state after the clients have connected. Messages that are sent to the SignalR service are propagated out to the clients in a hub pattern. You can also set messages to only go between specific clients. In the example here and what I'm going to be walking through, I setup a hub whereby all messages are sent to all clients connected to the SignalR service.&lt;/p&gt;

&lt;p&gt;This same setup could work with other frameworks than Angular. The key part is just using the &lt;a href="https://www.npmjs.com/package/@aspnet/signalr" rel="noopener noreferrer"&gt;@aspnet/signalr&lt;/a&gt; package and coordinating the handshake with the actual SignalR service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting this up in Azure
&lt;/h2&gt;

&lt;p&gt;So if you want to use the SignalR service with Angular, you first have to setup the infrastructure with Azure. One of the best parts is that Microsoft makes all of this very intuitive with the Azure Portal.&lt;/p&gt;

&lt;p&gt;You first just literally create the SignalR service:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-10-19-at-3.15.03-pm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-10-19-at-3.15.03-pm.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then you make sure to go over to Keys and capture the connection string that Azure uses when doing the initial handshake:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-10-19-at-3.16.35-pm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-10-19-at-3.16.35-pm.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then you create two Azure Functions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;negotiate&lt;/code&gt; to handle the initial handshake process&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;messages&lt;/code&gt; to literally transport messages once the handshake is good&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you use the JavaScript option, the functions are incredibly simple with the &lt;code&gt;negotiate&lt;/code&gt; looking like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-10-19-at-3.20.10-pm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-10-19-at-3.20.10-pm.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and then the &lt;code&gt;messages&lt;/code&gt; looking like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-10-19-at-3.20.51-pm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-10-19-at-3.20.51-pm.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You also need to add &lt;code&gt;AzureSignalRConnectionString&lt;/code&gt; as a application setting in your Azure Functions app instance:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-10-19-at-5.05.46-pm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-10-19-at-5.05.46-pm.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The last step with regards to infrastructure is just to enable CORS if you're testing with a URL etc. You can do this with the CORS setting in the services.&lt;/p&gt;

&lt;p&gt;I also highly recommend using the &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions" rel="noopener noreferrer"&gt;Azure Functions VSCode extension&lt;/a&gt; for developing the Azure Functions. It is super easy to work with, and makes building and deploying just a few steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Angular
&lt;/h2&gt;

&lt;p&gt;Once you have your infrastructure setup, now it's time to connect your Angular application.&lt;/p&gt;

&lt;p&gt;If you check out my &lt;a href="https://github.com/andrewevans0102/azure-signalr2" rel="noopener noreferrer"&gt;sample application GitHub repo&lt;/a&gt; you'll see this in action.&lt;/p&gt;

&lt;p&gt;I created a service that wraps the &lt;a href="https://www.npmjs.com/package/@aspnet/signalr" rel="noopener noreferrer"&gt;@aspnet/signalr&lt;/a&gt; package with two methods like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export class SignalRService {
  private readonly _http: HttpClient;
  // private readonly _baseUrl: string = "http://localhost:7071/api/";
  private readonly _baseUrl: string = environment.azureConnection;
  private hubConnection: HubConnection;
  messages: Subject&amp;lt;string&amp;gt; = new Subject();

  constructor(http: HttpClient) {
    this._http = http;
  }

  private getConnectionInfo(): Observable&amp;lt;SignalRConnectionInfo&amp;gt; {
    let requestUrl = `${this._baseUrl}negotiate`;
    return this._http.get&amp;lt;SignalRConnectionInfo&amp;gt;(requestUrl);
  }

  init() {
    this.getConnectionInfo().subscribe((info) =&amp;gt; {
      let options = {
        accessTokenFactory: () =&amp;gt; info.accessToken,
      };

      this.hubConnection = new signalR.HubConnectionBuilder()
        .withUrl(info.url, options)
        .configureLogging(signalR.LogLevel.Information)
        .build();

      this.hubConnection.start().catch((err) =&amp;gt; console.error(err.toString()));

      this.hubConnection.on("notify", (data: any) =&amp;gt; {
        this.messages.next(data);
      });
    });
  }

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

&lt;/div&gt;



&lt;p&gt;When the service initializes, it gets a reference to the SignalR endpoint that is exposed from the SignalR service and negotiates the handshake. It then leverages an Angular Subject to emit any new messages received from the "notify" event from SignalR.&lt;/p&gt;

&lt;p&gt;With regards to the actual messages, I chose to use localStorage to handle the conversation history as you see here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  send(message: string): Observable&amp;lt;void&amp;gt; {
    console.log("called2");
    let requestUrl = `${this._baseUrl}messages`;
    return this._http.post(requestUrl, message).pipe(map((result: any) =&amp;gt; {}));
  }

  receieve(message: Message): Message[] {
    // read in from local strorage
    const messages = this.load();
    messages.unshift(message);
    localStorage.setItem("messages", JSON.stringify(messages));
    return messages;
  }

  load(): Message[] {
    const messagesLocal = localStorage.getItem("messages");
    let messagesResponse = [];
    if (messagesLocal !== null) {
      messagesResponse = JSON.parse(messagesLocal);
    }
    return messagesResponse;
  }

  clear(): Observable&amp;lt;void&amp;gt; {
    const messagesLocal = localStorage.getItem("messages");
    let messagesResponse = [];
    if (messagesLocal !== null) {
      localStorage.setItem("messages", JSON.stringify(messagesResponse));
    }
    return of(null);
  }

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

&lt;/div&gt;



&lt;p&gt;In the actual components in the Angular application that operate the chat function, I create a reference to the SignalR service and handle the events that come in from the stream accordingly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    this.signalRService.messages.subscribe((message) =&amp;gt; {
      // create message
      const result = message.split("|");
      const sendMessage = new Message();
      sendMessage.sender = result[0];
      sendMessage.body = result[1];
      // this.messages.unshift(sendMessage);
      this.store.dispatch(
        MessagesActions.messageRecieved({ message: sendMessage })
      );
    });

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

&lt;/div&gt;



&lt;p&gt;I'm using &lt;a href="https://ngrx.io/" rel="noopener noreferrer"&gt;NgRx&lt;/a&gt; to handle the various flows in the application, and you see this here with the dispatch of the &lt;code&gt;messageReceived&lt;/code&gt; action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;So in this post I introduced how you can use Azure's SignalR service with Angular. It is a very powerful service, that you can easily integrate with your frontend applications.&lt;/p&gt;

&lt;p&gt;There are a lot of great things you can build with this technology. As I mentioned in the intro, this is great for any application that needs realtime feedback.&lt;/p&gt;

&lt;p&gt;I covered Angular here, but there are similar patterns that you could apply to the other major frontend libraries out there. I encourage you to look at my &lt;a href="https://github.com/andrewevans0102/azure-signalr" rel="noopener noreferrer"&gt;sample application&lt;/a&gt; to learn more.&lt;/p&gt;

&lt;p&gt;Thanks for reading my post! Follow me on &lt;a href="https://www.andrewevans.dev" rel="noopener noreferrer"&gt;andrewevans.dev&lt;/a&gt; and, feel free to message me on Twitter at &lt;a href="https://www.twitter.com/andrewevans0102" rel="noopener noreferrer"&gt;@AndrewEvans0102&lt;/a&gt; if you have any questions or wanted to learn more.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>angular</category>
      <category>signalr</category>
      <category>microsoft</category>
    </item>
    <item>
      <title>How to get Started with React Redux</title>
      <dc:creator>Andrew Evans</dc:creator>
      <pubDate>Thu, 15 Oct 2020 23:49:00 +0000</pubDate>
      <link>https://forem.com/andrewevans0102/how-to-get-started-with-react-redux-5e2k</link>
      <guid>https://forem.com/andrewevans0102/how-to-get-started-with-react-redux-5e2k</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fmandalorian_cockpit.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fmandalorian_cockpit.jpg" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The Mandalorian and Baby Yoda working with Redux on their ship the Razor Crest. The original image was copied from &lt;a href="https://www.cinemablend.com/television/2486422/the-only-star-wars-things-you-need-to-know-before-watching-the-mandalorian" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Redux is one of the most popular patterns that is in use in the frontend world today. You see the same pattern in not only React, but Angular and Vue as well. Redux is very powerful as it provides a routine way that you can manage state in your applications. Moreover, Redux scales as your projects get larger. So it works great for both small and enterprise applications.&lt;/p&gt;

&lt;p&gt;This post is going to walkthrough how to use Redux in your React applications. I'm going to assume that you understand some basics about &lt;a href="https://reactjs.org/docs/hooks-intro.html" rel="noopener noreferrer"&gt;React Hooks&lt;/a&gt; as I'm going to be using the &lt;code&gt;useState&lt;/code&gt;, &lt;code&gt;useEffect&lt;/code&gt;, &lt;code&gt;useSelector&lt;/code&gt; and &lt;code&gt;useDispatch&lt;/code&gt; hooks respectively.&lt;/p&gt;

&lt;p&gt;I'm also going to be walking through a sample project that I've setup at &lt;a href="https://www.github.com/andrewevans0102/intro-to-redux-lab2" rel="noopener noreferrer"&gt;my GitHub repo here&lt;/a&gt;. We will be walking through different phases of the same project. I'm going to walk you through (1) setting up Redux, (2) adding actions and reducers, and (3) creating side effects.&lt;/p&gt;

&lt;p&gt;As a Mandalorian fan myself, the sample project will be a mini fan site with pages for episode info, quotes, and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Redux Concepts
&lt;/h2&gt;

&lt;p&gt;So before we dive into using Redux, we should cover some vocabulary that we'll be using in the subsequent sections.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-10-16-at-8.36.38-am.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-10-16-at-8.36.38-am.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;image was copied from my article "How to Start Flying with Angular and NgRx" &lt;a href="https://indepth.dev/how-to-start-flying-with-angular-and-ngrx" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Redux is a way to centrally organize your applications state in what's called a &lt;code&gt;store&lt;/code&gt; (in the diagram that's the block in pink). The idea is that everything about your application will be stored there, and then you'll use &lt;code&gt;selectors&lt;/code&gt; in your components to access this state. The store is &lt;code&gt;immutable&lt;/code&gt; which means that it cannot change. When you "change" the store, you are actually generating a new version. This is a concept you see in functional programming, and sometimes can be hard for newer folks to understand. I highly recommend watching &lt;a href="https://www.youtube.com/watch?v=0if71HOyVjY" rel="noopener noreferrer"&gt;Russ Olsen's talk on Functional Programming here for more on this concept&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Your components fire what are called &lt;code&gt;actions&lt;/code&gt; that then go through &lt;code&gt;reducers&lt;/code&gt; to modify the values in the &lt;code&gt;store&lt;/code&gt;. The idea behind reducers is that the state is &lt;code&gt;reduced&lt;/code&gt; from an &lt;code&gt;action&lt;/code&gt;. An &lt;code&gt;action&lt;/code&gt; can be any event that your application does from initial loading of data to responding to a button click. The &lt;code&gt;reducers&lt;/code&gt; in your application handle the changes to the store that result.&lt;/p&gt;

&lt;p&gt;Your components also subscribe to &lt;code&gt;selectors&lt;/code&gt; which basically listen for any type of state change. Whenever the store updates, the &lt;code&gt;selectors&lt;/code&gt; receive the updates and allow you to render your components accordingly.&lt;/p&gt;

&lt;p&gt;Some &lt;code&gt;actions&lt;/code&gt; can generate "side effects" which are usually HTTP calls. This would be when you want to call an API to get values to put in the store. The flow there is that you would (1) fire an &lt;code&gt;action&lt;/code&gt;, (2) call an API through an &lt;code&gt;effect&lt;/code&gt;, and then return an &lt;code&gt;action&lt;/code&gt; that goes through a &lt;code&gt;reducer&lt;/code&gt; to modify the &lt;code&gt;store&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I know that this is a lot of vocabulary to start, but it will make more sense as we begin to add Redux to our application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting Out
&lt;/h2&gt;

&lt;p&gt;So if you &lt;a href="https://www.github.com/andrewevans0102/intro-to-redux-lab2" rel="noopener noreferrer"&gt;view my sample project&lt;/a&gt;, you'll find that it has the following folders:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;start&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;redux-setup&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;redux-actions&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;redux-http&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We're going to walkthrough the folders in the project in this order. We will begin in the &lt;code&gt;start&lt;/code&gt; folder, as that's a version of the application with no Redux at all. Then the three other folders are completed phases of this project:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;redux-setup&lt;/code&gt; is the &lt;code&gt;start&lt;/code&gt; with redux added and an initial set of actions, reducers, selectors, and effects for the &lt;code&gt;episodes&lt;/code&gt; page.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;redux-actions&lt;/code&gt; is the &lt;code&gt;start&lt;/code&gt; with the &lt;code&gt;episodes&lt;/code&gt; and &lt;code&gt;quotes&lt;/code&gt; actions, reducers, selectors, and effects setup.&lt;/li&gt;
&lt;li&gt;Finally, &lt;code&gt;redux_http&lt;/code&gt; includes a set of actions, reducers, selectors and an effect that makes an actual HTTP call.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you're finished, you'll have a mini Mandalorian fan page that includes a page for episodes, quotes, a video of Season 2, and even a way to send a contact message.&lt;/p&gt;

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

&lt;p&gt;We'll start by cloning the project, and then going into the &lt;code&gt;start&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;The initial project looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── README.md
├── ReduxFlow.png
├── package-lock.json
├── package.json
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── manifest.json
│   └── robots.txt
└── src
    ├── App.css
    ├── App.js
    ├── App.test.js
    ├── components
    │   ├── Header.js
    │   └── index.js
    ├── config
    │   ├── episodes.json
    │   └── quotes.json
    ├── index.css
    ├── index.js
    ├── logo.svg
    ├── pages
    │   ├── ContactPage.js
    │   ├── EpisodesPage.js
    │   ├── HomePage.jpg
    │   ├── HomePage.js
    │   ├── QuotesPage.js
    │   ├── Season2Page.js
    │   └── index.js
    ├── serviceWorker.js
    ├── setupTests.js
    └── styles
        ├── _contact.scss
        ├── _episodes.scss
        ├── _header.scss
        ├── _home.scss
        ├── _quotes.scss
        ├── _season2.scss
        └── styles.scss

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

&lt;/div&gt;



&lt;p&gt;The first step is to add Redux to your application and then install the necessary libraries. Go ahead and install the libraries with npm by doing the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i react-redux
npm i redux
npm i redux-devtools-extension
npm i redux-thunk

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

&lt;/div&gt;



&lt;p&gt;Now, I also recommend the &lt;a href="https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd" rel="noopener noreferrer"&gt;Redux DevTools extension for Chrome&lt;/a&gt; as that will help you see what happens with your store. I recommend installing that at this phase as well.&lt;/p&gt;

&lt;p&gt;So now with your libraries installed, let's go over to the &lt;code&gt;src/index.js&lt;/code&gt; file to setup our &lt;code&gt;store&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To add Redux to React, you first need to wrap your entry component with a &lt;code&gt;Provider&lt;/code&gt; as you see here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// step 1 add these imports
import { Provider } from 'react-redux';
import configureStore from './redux/configureStore';

const initialState = {};
const { store } = configureStore(initialState);

ReactDOM.render(
    // step 2 wrap your app in the Provider here
    // &amp;lt;React.StrictMode&amp;gt;
    // &amp;lt;App /&amp;gt;
    // &amp;lt;/React.StrictMode&amp;gt;,
    &amp;lt;Provider store={store}&amp;gt;
        &amp;lt;App /&amp;gt;
    &amp;lt;/Provider&amp;gt;,
    document.getElementById('root')
);

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

&lt;/div&gt;



&lt;p&gt;Now, you'll notice that we're referencing a &lt;code&gt;redux&lt;/code&gt; folder that hasn't been created yet. You'll need to ahead and set that up so we can begin the &lt;code&gt;actions&lt;/code&gt;, &lt;code&gt;reducers&lt;/code&gt;, and eventually &lt;code&gt;effects&lt;/code&gt; that we'll be using.&lt;/p&gt;

&lt;p&gt;Go ahead and create a &lt;code&gt;src/redux&lt;/code&gt; folder as this will be where we put our Redux implementation. Now create the &lt;code&gt;src/redux/configureStore.js&lt;/code&gt; file as you see here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';
import rootReducer from './reducers/index';

const middleware = [thunk];
const enhancers = [];

// create enhancers to include middleware
// thunk allows you to dispatch functions between the actions
const composedEnhancers = composeWithDevTools(
    applyMiddleware(...middleware),
    ...enhancers
);

// create the store and return it to the application onload
// note that here we are including our reducers to setup our store and interactions across the application
export default function configureStore(initialState) {
    const store = createStore(rootReducer, initialState, composedEnhancers);

    return { store };
}

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

&lt;/div&gt;



&lt;p&gt;As the comments point out, we first use the &lt;code&gt;redux-devtools-extension&lt;/code&gt; library to create &lt;code&gt;enhancers&lt;/code&gt; that we will use with Redux. This is a common way to start building your store, but there are other methods and enhancers you can include.&lt;/p&gt;

&lt;p&gt;Then we create the &lt;code&gt;configureStore&lt;/code&gt; method by using the &lt;code&gt;createStore&lt;/code&gt; to build a root reducer and an initial state with our enhancers. Also note that we are using the &lt;a href="https://github.com/reduxjs/redux-thunk" rel="noopener noreferrer"&gt;redux thunk&lt;/a&gt; middleware so that we can return functions instead of just actions with our flows. There are a lot of options with middleware beyond thunk, but this is all we'll need for our application.&lt;/p&gt;

&lt;p&gt;Once you've got &lt;code&gt;configureStore&lt;/code&gt; all setup, let's go ahead and create our reducers folder in &lt;code&gt;src/redux&lt;/code&gt;. Inside that folder create &lt;code&gt;src/redux/reducers/index.js&lt;/code&gt; file with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { combineReducers } from 'redux';

export default combineReducers({
});

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

&lt;/div&gt;



&lt;p&gt;Now we've got the basic shell setup, and we have basically an empty store with no initial state except for &lt;code&gt;{}&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the Actions
&lt;/h2&gt;

&lt;p&gt;So with this basic shell, we now can go ahead and add actions. We're going to setup the &lt;code&gt;episodes&lt;/code&gt; actions for the site.&lt;/p&gt;

&lt;p&gt;Go ahead and create an &lt;code&gt;actions&lt;/code&gt; and &lt;code&gt;actionTypes&lt;/code&gt; folder in the &lt;code&gt;src/redux&lt;/code&gt; folder that we created before.&lt;/p&gt;

&lt;p&gt;Inside &lt;code&gt;actionTypes&lt;/code&gt; folder create an &lt;code&gt;Episodes.js&lt;/code&gt; file and copy and paste the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const GET_EPISODES = 'episodes/GET_EPISODES';
export const SET_EPISODES = 'episodes/SET_EPISODES';
export const EPISODES_ERROR = 'episodes/EPISODES_ERROR';

export const initialEpisodesState = {
    episodes: [],
    errors: [],
};

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

&lt;/div&gt;



&lt;p&gt;I'm also using JavaScript modules, so add a &lt;code&gt;index.js&lt;/code&gt; file next to it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import * as EpisodesActionTypes from './Episodes';

export { EpisodesActionTypes };

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

&lt;/div&gt;



&lt;p&gt;What is this doing? This is defining the action types we'll be using in our application. Notice that it is very simple and we have a &lt;code&gt;GET_EPISODES&lt;/code&gt; and &lt;code&gt;SET_EPISODES&lt;/code&gt; action along with an &lt;code&gt;EPISODES_ERROR&lt;/code&gt; message. The &lt;code&gt;initialEpisodesState&lt;/code&gt; is just defining what our store will look like when the application loads.&lt;/p&gt;

&lt;p&gt;Next lets actually define the actions in a file &lt;code&gt;src/redux/actions/Episodes.js&lt;/code&gt; file like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { EpisodesActionTypes } from '../actionTypes';
import episodes from '../../config/episodes';

export function getEpisodes() {
    return { type: EpisodesActionTypes.GET_EPISODES };
}

export function setEpisodes(episodes) {
    return { type: EpisodesActionTypes.SET_EPISODES, episodes };
}

export function episodesError() {
    return { type: EpisodesActionTypes.GET_EPISODES };
}

// here we introduce a side effect
// best practice is to have these alongside actions rather than an "effects" folder
export function retrieveEpisodes() {
    return function (dispatch) {
        // first call get about to clear values
        dispatch(getEpisodes());
        // return a dispatch of set while pulling in the about information (this is considered a "side effect")
        return dispatch(setEpisodes(episodes));
    };
}

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

&lt;/div&gt;



&lt;p&gt;I'm also using JavaScript modules, so add a &lt;code&gt;index.js&lt;/code&gt; file next to it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import * as EpisodesActions from './Episodes';

export { EpisodesActions };

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

&lt;/div&gt;



&lt;p&gt;So as you see here, we're defining a &lt;code&gt;getEpisodes&lt;/code&gt; function that corresponds to the &lt;code&gt;GET_EPISODES&lt;/code&gt; action, a &lt;code&gt;setEpisodes&lt;/code&gt; function that corresponds to the &lt;code&gt;SET_EPISODES&lt;/code&gt; action, a &lt;code&gt;episodesError&lt;/code&gt; that corresponds to the &lt;code&gt;EPISODES_ERROR&lt;/code&gt; action, and finally a side effect to &lt;code&gt;retrieveEpisodes&lt;/code&gt; which will pull them from a local configuration file.&lt;/p&gt;

&lt;p&gt;There are differing opinions as to where to place side effects in React projects. From the documentation I found on &lt;a href="https://react-redux.js.org/" rel="noopener noreferrer"&gt;React Redux&lt;/a&gt; I found it was recommended to place them alongside your actions. In practice, I've experienced that having the side effects near your actions makes it easy as a developer to find and maintain them. In a more general sense, since React is a library, you can organize your application as you see fit and put them wherever it best works for you.&lt;/p&gt;

&lt;p&gt;So now that we've defined our action types and actions, let's add reducers that use those actions. Create a &lt;code&gt;src/redux/reducers/Episodes.js&lt;/code&gt; file as you see here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { EpisodesActionTypes } from '../actionTypes';

function Episodes(state = EpisodesActionTypes.initialEpisodesState, action) {
    switch (action.type) {
        case EpisodesActionTypes.GET_EPISODES:
            return Object.assign({}, state, {
                loading: true,
                episodes: [],
            });
        case EpisodesActionTypes.SET_EPISODES:
            return Object.assign({}, state, {
                ...state,
                loading: false,
                episodes: action.episodes,
            });
        case EpisodesActionTypes.EPISODES_ERROR:
            return Object.assign({}, state, {
                ...state,
                errors: [...state.errors, action.error],
            });
        default:
            return state;
    }
}

export default Episodes;

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

&lt;/div&gt;



&lt;p&gt;Since I'm using JavaScript modules, go ahead and modify the &lt;code&gt;index.js&lt;/code&gt; file we had before to include the &lt;code&gt;Episodes.js&lt;/code&gt; file as you see here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { combineReducers } from 'redux';
import Episodes from './Episodes';

export default combineReducers({
    Episodes,
});

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

&lt;/div&gt;



&lt;p&gt;What is all of this doing? The reducers are keyed based on action type. If you notice, the value that is returned from the action is then applied to the necessary place in the state. So in the case of &lt;code&gt;SET_EPISODES&lt;/code&gt; you'll note that it is taking the action payload and putting it into the &lt;code&gt;episodes&lt;/code&gt; portion of the state as you see here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;case EpisodesActionTypes.SET_EPISODES:
    return Object.assign({}, state, {
        ...state,
        loading: false,
        episodes: action.episodes,
    });

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Connecting Redux to Your Components
&lt;/h2&gt;

&lt;p&gt;So now we have all the pieces together, but we still need to add Redux to our actual components. So let's modify the &lt;code&gt;src/pages/EpisodesPage.js&lt;/code&gt; as you see here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { EpisodesActions } from '../redux/actions';
import '../styles/styles.scss';
// import episodes from '../config/episodes';

// const episodes = [
// { key: 'first', value: 'something here' },
// { key: 'second', value: 'something there' },
// ];

function EpisodesPage(props) {
    const dispatch = useDispatch();

    // first read in the values from the store through a selector here
    const episodes = useSelector((state) =&amp;gt; state.Episodes.episodes);

    useEffect(() =&amp;gt; {
        // if the value is empty, send a dispatch action to the store to load the episodes correctly
        if (episodes.length === 0) {
            dispatch(EpisodesActions.retrieveEpisodes());
        }
    });

    return (
        &amp;lt;section className="episodes"&amp;gt;
            &amp;lt;h1&amp;gt;Episodes&amp;lt;/h1&amp;gt;
            {episodes !== null &amp;amp;&amp;amp;
                episodes.map((episodesItem) =&amp;gt; (
                    &amp;lt;article key={episodesItem.key}&amp;gt;
                        &amp;lt;h2&amp;gt;
                            &amp;lt;a href={episodesItem.link}&amp;gt;{episodesItem.key}&amp;lt;/a&amp;gt;
                        &amp;lt;/h2&amp;gt;
                        &amp;lt;p&amp;gt;{episodesItem.value}&amp;lt;/p&amp;gt;
                    &amp;lt;/article&amp;gt;
                ))}
            &amp;lt;div className="episodes__source"&amp;gt;
                &amp;lt;p&amp;gt;
                    original content copied from
                    &amp;lt;a href="https://www.vulture.com/tv/the-mandalorian/"&amp;gt;
                        here
                    &amp;lt;/a&amp;gt;
                &amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/section&amp;gt;
    );
}

export default EpisodesPage;

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

&lt;/div&gt;



&lt;p&gt;As you'll note there are a few changes that make Redux possible. First note that we are pulling in the necessary hooks at the top with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { EpisodesActions } from '../redux/actions';

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

&lt;/div&gt;



&lt;p&gt;Next you'll note that we commented out the pull of the episodes information locally and instead are retrieving it from a selector:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// import episodes from '../config/episodes';

// const episodes = [
// { key: 'first', value: 'something here' },
// { key: 'second', value: 'something there' },
// ];

function EpisodesPage(props) {
    const dispatch = useDispatch();

    // first read in the values from the store through a selector here
    const episodes = useSelector((state) =&amp;gt; state.Episodes.episodes);

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

&lt;/div&gt;



&lt;p&gt;Next you'll notice the use of &lt;code&gt;useEffect&lt;/code&gt; which dispatches a &lt;code&gt;retrieveEpisodes&lt;/code&gt; action as on load:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    useEffect(() =&amp;gt; {
        // if the value is empty, send a dispatch action to the store to load the episodes correctly
        if (episodes.length === 0) {
            dispatch(EpisodesActions.retrieveEpisodes());
        }
    });

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

&lt;/div&gt;



&lt;p&gt;So now, if you run the application, and then go to the Episodes page you should see it in action. If you open the Redux Devtools Extension you'll see the flow:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-10-15-at-9.36.46-pm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-10-15-at-9.36.46-pm.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So what happened and how does this work?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;On load, you initialized your store with an area for episodes&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;EpisodesPage&lt;/code&gt; component has subscribed to the store to listen for any new state changes&lt;/li&gt;
&lt;li&gt;When you click on the "Episodes" page the &lt;code&gt;retrieveEpisodes&lt;/code&gt; action fired which then actually triggers a side effect to first call &lt;code&gt;GET_EPISODES&lt;/code&gt; to clear the episodes in the store and then &lt;code&gt;SET_EPISODES&lt;/code&gt; which retrieves them from the config file and returns them to the component&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;EpisodesPage&lt;/code&gt; component receives the new store and renders the component&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;If your end result did not do the above flow, please check out the &lt;a href="https://www.github.com/andrewevans0102/intro-to-redux-lab2/tree/master/redux-setup" rel="noopener noreferrer"&gt;redux-setup&lt;/a&gt; folder and see the end product there.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Adding Quotes
&lt;/h2&gt;

&lt;p&gt;So now that you've got the episodes covered, you can now add quotes. The process is very similar and you'll create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;src/redux/actions/Quotes.js&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src/redux/actionsTypes/Quotes.js&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src/redux/actions/reducers/Quotes.js&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then in the &lt;code&gt;QuotesPage&lt;/code&gt; component you'll setup the same &lt;code&gt;action --&amp;gt; effect --&amp;gt; action --&amp;gt; reducer&lt;/code&gt; flow that we did before.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const dispatch = useDispatch();

// first read in the values from the store through a selector here
const quotes = useSelector((state) =&amp;gt; state.Quotes.quotes);

useEffect(() =&amp;gt; {
    // if the value is empty, send a dispatch action to the store to load the episodes correctly
    if (quotes.length === 0) {
        dispatch(QuotesActions.retrieveQuotes());
    }
});

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

&lt;/div&gt;



&lt;p&gt;To attempt to keep this post manageable, I won't add the implementation details here. I'll refer you to look at the &lt;a href="https://www.github.com/andrewevans0102/intro-to-redux-lab2/tree/master/redux-actions" rel="noopener noreferrer"&gt;redux-actions&lt;/a&gt; folder for what the finished product looks like.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding HTTP
&lt;/h2&gt;

&lt;p&gt;So up until now the two flows that you've seen for &lt;code&gt;episodes&lt;/code&gt; and &lt;code&gt;quotes&lt;/code&gt; used local files and did not make any HTTP calls. One of the most common usecases you see with React Redux is to make HTTP calls to handle interactions with APIs.&lt;/p&gt;

&lt;p&gt;If you go into the &lt;a href="https://www.github.com/andrewevans0102/intro-to-redux-lab2/tree/master/redux-http" rel="noopener noreferrer"&gt;redux-http&lt;/a&gt; folder you'll see an example where we add HTTP calls for the "contact" page of the site.&lt;/p&gt;

&lt;p&gt;The contact page actually adds messages to the &lt;a href="https://intro-to-redux-lab.web.app" rel="noopener noreferrer"&gt;page here&lt;/a&gt;. So when you've got this setup, you can see it in action by opening that page up alongside your local application.&lt;/p&gt;

&lt;p&gt;When making HTTP calls with React Redux, the general best practice is to put the side effect alongside the actions. If you look in the &lt;code&gt;redux&lt;/code&gt; folder you'll see Contact Actions, ActionTypes, and Reducers that are created.&lt;/p&gt;

&lt;p&gt;A good convention to use with redux is to have an action that initializes the process, a second action that actually calls the process, and then a &lt;code&gt;success&lt;/code&gt; and &lt;code&gt;failure&lt;/code&gt; action to suit. You can see this here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// here we introduce a side effect
// best practice is to have these alongside actions rather than an "effects" folder
export function sendContact(contact) {
    return function (dispatch) {
        // first call sending contact to start the process
        dispatch(sendingContact(contact));
        // actually call the HTTP endpoint here with the value to send
        return axios
            .post(contactEndpoint, contact)
            .then((response) =&amp;gt; {
                dispatch(contactSuccess(response));
            })
            .catch((error) =&amp;gt; {
                dispatch(contactError(error));
            });
    };
}

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

&lt;/div&gt;



&lt;p&gt;If you notice the &lt;code&gt;sendContact&lt;/code&gt; action is called, then it calls &lt;code&gt;sendingContact&lt;/code&gt; and then it makes the HTTP call and responds with either a &lt;code&gt;contactSuccess&lt;/code&gt; or &lt;code&gt;contactError&lt;/code&gt; response.&lt;/p&gt;

&lt;p&gt;Once you've built out the redux parts, you can connect it to your component like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const dispatch = useDispatch();

// when you make the rest call, the response can be seen in the selector here
const response = useSelector((state) =&amp;gt; state.Contact.response);

// when an error occurs it should appear here
const errors = useSelector((state) =&amp;gt; state.Contact.errors);

const handleSubmit = (event) =&amp;gt; {
    setProgress(true);
    event.preventDefault();
    const sendMessage = { firstName, lastName, message };
    dispatch(ContactActions.sendContact(sendMessage));
    // axios
    // .post(messageEndpoint, sendMessage)
    // .then((response) =&amp;gt; {
    // alert('success');
    // setProgress(false);
    // })
    // .catch((error) =&amp;gt; {
    // alert('error');
    // setProgress(false);
    // });
};

useEffect(() =&amp;gt; {
    if (response !== undefined) {
        setProgress(false);
    }

    if (errors.length &amp;gt; 0) {
        setProgress(false);
    }
}, [response, errors]);

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

&lt;/div&gt;



&lt;p&gt;Then in your template you can catch the response or errors with a check on the &lt;code&gt;selectors&lt;/code&gt; as happens with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    response !== undefined &amp;amp;&amp;amp; (
        &amp;lt;article className="contact__response"&amp;gt;
            Success with a return of {response.status.toString()}
        &amp;lt;/article&amp;gt;
    );
}
{
    errors.length &amp;gt; 0 &amp;amp;&amp;amp; (
        &amp;lt;article className="contact__error"&amp;gt;
            Error occured with message "{errors[0].message}"
        &amp;lt;/article&amp;gt;
    );
}

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

&lt;/div&gt;



&lt;p&gt;This pattern scales well, and can be used throughout the HTTP calls in your components.&lt;/p&gt;

&lt;p&gt;Again, to keep this post necessarily brief I'll refer you to the implementation in the &lt;a href="https://www.github.com/andrewevans0102/intro-to-redux-lab2/tree/master/redux-http" rel="noopener noreferrer"&gt;redux-http&lt;/a&gt; folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;So as you see with this project, once you understand the parts to Redux it's not hard to follow the pattern. In our project we setup episodes, quotes, and even a contact page that used Redux in the process.&lt;/p&gt;

&lt;p&gt;As I stated in the intro, this pattern enables you to have a common method of handling your applications state as you build more features and move it through its lifecycle. I have personally found that this pattern makes maintenance much easier than manually handling application state through custom services and event interactions.&lt;/p&gt;

&lt;p&gt;I hope that this post and my sample project helped you in your journey to learn more about Redux. I recommend playing with the example project I have here, and building out additional pages or features to learn the process.&lt;/p&gt;

&lt;p&gt;Thanks for reading my post! Follow me on &lt;a href="https://www.andrewevans.dev" rel="noopener noreferrer"&gt;andrewevans.dev&lt;/a&gt; and, feel free to message me on Twitter at &lt;a href="https://www.twitter.com/andrewevans0102" rel="noopener noreferrer"&gt;@AndrewEvans0102&lt;/a&gt; if you have any questions or wanted to learn more.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>redux</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Rebuilding my Site with Netlify and Next.js</title>
      <dc:creator>Andrew Evans</dc:creator>
      <pubDate>Mon, 08 Jun 2020 16:33:06 +0000</pubDate>
      <link>https://forem.com/andrewevans0102/rebuilding-my-site-with-netlify-and-next-js-4ij6</link>
      <guid>https://forem.com/andrewevans0102/rebuilding-my-site-with-netlify-and-next-js-4ij6</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Frebuilt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Frebuilt.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For quite some time now, I have wanted to rebuild my primary blogging site (&lt;a href="http://www.rhythmandbinary.com" rel="noopener noreferrer"&gt;www.rhythmandbinary.com&lt;/a&gt;) - I was previously using &lt;a href="https://wordpress.com/" rel="noopener noreferrer"&gt;WordPress&lt;/a&gt;, but decided I wanted to host my own content and have more control over the publishing process. And so finally, over the past few weeks, I have rebuilt this site using &lt;a href="https://www.netlify.com/" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt; and &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; and wanted to highlight some cool things I learned along the way.&lt;/p&gt;

&lt;p&gt;I chose Next.js because it has a lot of built in features that work well for static sites (&lt;a href="https://github.com/vercel/next.js/tree/canary/examples/blog-starter" rel="noopener noreferrer"&gt;check out their blog site example here&lt;/a&gt;). I also used Netlify because of the ease of their pipeline, and a lot of their built in features which make hosting and writing super fun.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Build to Publish
&lt;/h2&gt;

&lt;p&gt;With Netlify, you get a super user friendly console to manage your site out of the box. You connect Netlify to your site's GitHub repo, and set up a pipeline that listens to your repo's master branch. Then whenever you merge a PR into master, Netlify will kickoff a build and deploy of your site. You can customize this for more specialized setups, but this feature alone took a lot of the trouble out of managing a pipeline. There are also a ton of great features with Netlify, I recommend &lt;a href="https://docs.netlify.com/?_ga=2.172710187.965183352.1591624137-733159775.1589712148" rel="noopener noreferrer"&gt;checking out their docs for more&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since this site is a blog, I wanted some form of a CMS to make writing posts easier. After some googling I found &lt;a href="https://www.netlifycms.org/" rel="noopener noreferrer"&gt;Netlify CMS&lt;/a&gt;! It works nicely alongside Netlify hosting, and comes with a WYSIWYG editor that you can easily access directly from your site. Whenever you write posts, Netlify CMS makes PRs directly to your GitHub repo. It even has a publish workflow so you can work on posts over time, rather than having to write everything down in one setting. If you setup the workflow option, whenever you "save" Netlify's CMS pushes a commit to the PR that it opened.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-06-09-at-9.59.02-am.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-06-09-at-9.59.02-am.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-06-08-at-1.26.25-pm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-06-08-at-1.26.25-pm.png" alt="GitHub Commits" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To get started, you just need to enable Netlify to have access to your project's GitHub repo, and then add an associated yaml file for Netlify to read to understand how to work with your site. To get this setup with Next.js is super easy, &lt;a href="https://www.netlifycms.org/docs/nextjs/" rel="noopener noreferrer"&gt;check out Netlify's tutorial here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The end product of using both Netlify CMS and Hosting is this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-06-08-at-2.10.40-pm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-06-08-at-2.10.40-pm.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The best part is that all of this is free and I still control all the source content. So you can actually edit posts directly in your project or through the editor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving Posts from WordPress to Here
&lt;/h2&gt;

&lt;p&gt;I had over 50 posts on my previous WordPress site that I had to move over. To do this, I used a little bit of JavaScript magic and leveraged &lt;a href="https://www.npmjs.com/package/rss-parser" rel="noopener noreferrer"&gt;the npm rss-parser package&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/turndown" rel="noopener noreferrer"&gt;turndown&lt;/a&gt; to basically read the RSS feed from my old site and make new markdown files. Once the RSS feed was converted into markdown, I used RegEx to replace punctuation and a few things I saw when initially testing the program. Then I used the Node.js "fs" package to write markdown files locally line by line. This was important since I'm also using Netlify's CMS, I'd need each post to have some metadata at the top of each markdown file. Despite leveraging all of this automation, I still ended up having to take an afternoon of walking through all the posts and fixing indenting errors and various image issues. That being said, this little bit of automation really helped. Here's the program I wrote to make it happen:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const Parser = require("rss-parser");
const parser = new Parser();
const fs = require("fs");
const TurndownService = require("turndown");
const turndownService = new TurndownService();
const striptags = require("striptags");

const callRSS = async (feedSource) =&amp;gt; {
  const feedOutput = await parser.parseURL(feedSource);
  const output = feedOutput.items;
  return output;
};

const parseOutput = async () =&amp;gt; {
  const feedSource = "https://rhythmandbinary.com/rss";
  const output = await callRSS(feedSource);
  return output;
};

const retrievePostsSaveMarkdown = async () =&amp;gt; {
  const output = await parseOutput();

  output.forEach((post) =&amp;gt; {
    // fileName
    const blankSpace = " ";
    const blankSpaceRegEx = new RegExp(blankSpace, "g");
    let fileName = post.title;
    fileName = fileName.replace(blankSpaceRegEx, "_");
    const singleColon = ":";
    const singleColonRegEx = new RegExp(singleColon, "g");
    fileName = fileName.replace(singleColonRegEx, "");
    fileName = fileName.replace(/,/g, "");
    fileName = fileName.replace(/\./g, "");
    fileName = fileName.replace(/[(.)]/gm, "");
    fileName = fileName.replace(/[(?)]/gm, "");
    fileName = fileName.replace(/[(!)]/gm, "");

    // title
    let title = post.title;
    title = title.replace(singleColonRegEx, "");

    // postDate
    const postDate = post.pubDate;

    // snippet
    let snippet = striptags(post["content:encoded"]);
    if (snippet.length &amp;gt; 200) {
      snippet = snippet.substring(0, 200);
    }
    const singleQuote = /(&amp;amp;#8217;)/gm;
    snippet = snippet.replace(singleQuote, "'");
    const leftQuote = /(&amp;amp;#8220;)/gm;
    snippet = snippet.replace(leftQuote, "");
    const rightQuote = /(&amp;amp;#8221;)/gm;
    snippet = snippet.replace(rightQuote, "");
    const imageSource = /(\(image source\))/gm;
    snippet = snippet.replace(imageSource, "");
    const multiSpace = /[\n]{2,}/gm;
    const multiSpaceRegEx = new RegExp(multiSpace, "g");
    snippet = snippet.replace(multiSpaceRegEx, "");

    // content
    const content = turndownService.turndown(post["content:encoded"]);

    fs.appendFileSync(`${__dirname}/../_posts/${fileName}.md`, "--- \n");
    fs.appendFileSync(
      `${__dirname}/../_posts/${fileName}.md`,
      "layout: blog \n"
    );
    fs.appendFileSync(
      `${__dirname}/../_posts/${fileName}.md`,
      `title: ${title} \n`
    );
    fs.appendFileSync(
      `${__dirname}/../_posts/${fileName}.md`,
      `date: ${postDate} \n`
    );
    fs.appendFileSync(
      `${__dirname}/../_posts/${fileName}.md`,
      `snippet: \"${snippet}\" \n`
    );
    fs.appendFileSync(`${__dirname}/../_posts/${fileName}.md`, "--- \n");
    fs.appendFileSync(`${__dirname}/../_posts/${fileName}.md`, `${content} \n`);
  });
};

retrievePostsSaveMarkdown();

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Generating my own RSS Feed
&lt;/h2&gt;

&lt;p&gt;As part of my new site, I knew I wanted to have an RSS feed. RSS feeds are great for followers and also for times when you want to retrieve a copy of all the content easily. To do this, I actually leveraged the &lt;a href="https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation" rel="noopener noreferrer"&gt;getStaticProps()&lt;/a&gt; method that came with Next.js.&lt;/p&gt;

&lt;p&gt;One of the coolest part's of Next.js is that it prebuilds a site to optimize performance. So that means if you use Next.js for your site, you're given a set of APIs that are called only at build time. When building static sites, this is very powerful because it lets you orchestrate data using all of the features you have on your local filesystem. So for my RSS feed, I created an "/rss" route which would retrieve all of my posts locally and build an XML file which I can then share directly form my site (&lt;a href="https://nextjs.org/docs/basic-features/static-file-serving" rel="noopener noreferrer"&gt;see the Next.js docs on static file serving here&lt;/a&gt;). This ended up looking somewhat like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export async function getStaticProps() {
  const allPosts = getAllPosts(["title", "date", "slug", "content", "snippet"]);

  allPosts.forEach(async (post) =&amp;gt; {
    unified()
      .use(markdown)
      .use(html)
      .process(post.content, function (err, file) {
        if (err) throw err;
        post.content = file;
      });
  });

  const XMLPosts = getRssXml(allPosts);
  saveRSSXMLPosts(XMLPosts);

  return {
    props: { XMLPosts },
  };
}

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

&lt;/div&gt;



&lt;p&gt;The specific file saving looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export function saveRSSXMLPosts(XMLPosts) {
  const publicDirectory = join(process.cwd(), "public/rss.xml");
  fs.writeFileSync(publicDirectory, XMLPosts);
}

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

&lt;/div&gt;



&lt;p&gt;To do the actual building of the RSS feed I had to do a fair amount of googling. I found &lt;a href="https://www.bergqvist.it/blog/2019/12/2/add-rss-feed-to-nextjs" rel="noopener noreferrer"&gt;this post super helpful&lt;/a&gt; in getting setup. I also used the &lt;a href="https://validator.w3.org/feed/" rel="noopener noreferrer"&gt;W3C official RSS feed site&lt;/a&gt; to verify that my XML file was compliant with the standard (it is!).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-06-08-at-1.45.51-pm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-06-08-at-1.45.51-pm.png" alt="RSS Validator" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom Navigation
&lt;/h2&gt;

&lt;p&gt;I also built my own paging with custom navigation. I highlight this because it took quite a while to get the paging to work correctly. I know there are a lot of packages that have paging components, but I wanted to write my own. Doing this took a little bit of magic to manage finding the location and "page" of posts. I also made it responsive so that it looked good both in mobile and desktop views. To actually build my pages, I also used the "getStaticProps()" method that I mentioned earlier, and then built a local array that the main page on the site uses. The end result was a nice little set of page buttons:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-06-09-at-10.11.20-am.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-06-09-at-10.11.20-am.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Liking Posts
&lt;/h2&gt;

&lt;p&gt;With my new site, I wanted a way for readers to "like" posts. So to do this I actually built a whole API with Firebase to save likes for each post. This was pretty straightforward. Just go to the little heart at the bottom of each post to see it in action.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-06-08-at-2.21.25-pm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.rhythmandbinary.com%2Fimages%2Fuploads%2Fscreen-shot-2020-06-08-at-2.21.25-pm.png" alt="Like Picture" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Some Other Cool Features
&lt;/h2&gt;

&lt;p&gt;In addition to the features I've highlighted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I added a search feature that searches both the titles and content of my posts (using the standard JS filter method)&lt;/li&gt;
&lt;li&gt;I made the site fully responsive. Special thanks to Mrs. Natalie Evans (my wife and best post reviewer around) for doing design reviews with me.&lt;/li&gt;
&lt;li&gt;I made use of SCSS maps wherever possible. This was super helfpul as I was building the site's theme and also making it responsive.&lt;/li&gt;
&lt;li&gt;Setup Google Analytics with regular reports. Next.js had a great example in &lt;a href="https://github.com/vercel/next.js/tree/canary/examples/with-react-ga" rel="noopener noreferrer"&gt;their GitHub repo that got me going&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;So I will be using this new site as my primary blog platform from now on. Building my own site was quite an undertaking, but it is really cool to be able to say you built your own setup. From here, just follow this site and stay tuned for new posts. I have a Twitter feed that you can follow for any new posts I write (&lt;a href="https://twitter.com/rhythmandbinary" rel="noopener noreferrer"&gt;check it out here&lt;/a&gt;). You can also feel free to contact me on &lt;a href="https://andrewevans.dev/" rel="noopener noreferrer"&gt;andrewevans.dev&lt;/a&gt;. Thanks again for reading my post!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Video Conferencing</title>
      <dc:creator>Andrew Evans</dc:creator>
      <pubDate>Mon, 23 Mar 2020 12:25:08 +0000</pubDate>
      <link>https://forem.com/andrewevans0102/video-conferencing-5443</link>
      <guid>https://forem.com/andrewevans0102/video-conferencing-5443</guid>
      <description>&lt;p&gt;In light of everything going on with the Coronavirus, I thought it might be helpful to do a short post covering different video conferencing applications. Many folks are working remotely, and it would help to have a quick rundown of some of the tools available. This list is by no means exhaustive, but hopefully will get you started with your home or business needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Apple FaceTime
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F46rviz0mcood514eem91.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F46rviz0mcood514eem91.jpg" width="800" height="451"&gt;&lt;/a&gt;(photo copied from Apple’s &lt;a href="https://support.apple.com/en-us/HT204380" rel="noopener noreferrer"&gt;site here &lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This is a given if you’re an Apple user. It is very straightforward and built into iOS. You can turn it on with your phone or other Apple device by going settings –&amp;gt; FaceTime.&lt;/p&gt;

&lt;p&gt;You can call people directly or with their email. You can even use your &lt;a href="https://support.apple.com/en-us/HT208986" rel="noopener noreferrer"&gt;Memoji&lt;/a&gt; as your avatar when talking.&lt;/p&gt;

&lt;p&gt;I’ve had a great experience with this, and this is great for both individual and group chats.&lt;/p&gt;

&lt;p&gt;For more info go to the official &lt;a href="https://support.apple.com/en-us/HT204380" rel="noopener noreferrer"&gt;Apple support page here&lt;/a&gt;. If you want to do a group chat, then go to the link &lt;a href="https://support.apple.com/en-us/HT209022" rel="noopener noreferrer"&gt;here for more info&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zoom
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmgmndrb2a4b8wvob0lmm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmgmndrb2a4b8wvob0lmm.jpg" alt="Image result for zoom" width="800" height="450"&gt;&lt;/a&gt;(image originally copied from &lt;a href="https://d.newsweek.com/en/full/1573796/zoom-how-use-online-classes.jpg" rel="noopener noreferrer"&gt;here&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;I’ve used Zoom both in personal and professional settings. It has a super intuitive UI and works very well as long as you have a stable internet connection.&lt;/p&gt;

&lt;p&gt;To get started just go to &lt;a href="https://zoom.us/meetings" rel="noopener noreferrer"&gt;https://zoom.us/meetings&lt;/a&gt; and create a free account. Then you can either just use the browser or install desktop apps for your platform.&lt;/p&gt;

&lt;p&gt;If you go over to the plans section you’ll see that you can get a lot with a free plan including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unlimited 1 to 1 meetings&lt;/li&gt;
&lt;li&gt;40 mins limit on group meetings&lt;/li&gt;
&lt;li&gt;Application Sharing&lt;/li&gt;
&lt;li&gt;Custom Backgrounds (super cool feature if you turn it on)&lt;/li&gt;
&lt;li&gt;Recording of video and audio if you enable it&lt;/li&gt;
&lt;li&gt;Many other online collaboration options&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The only real challenge with Zoom is that its only free for 1 on 1 calls. If you want to do a group chat (more than 2 people) then you only get 40 mins free.&lt;/p&gt;

&lt;p&gt;I personally prefer Zoom if I’m meeting with someone one on one or providing IT support. The features are very intuitive and its easy to get up and running in no time.&lt;/p&gt;

&lt;p&gt;For more on Zoom go here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://zoom.us/meetings" rel="noopener noreferrer"&gt;Home Page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zoom.us/pricing" rel="noopener noreferrer"&gt;Pricing Plans&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Google Hangouts
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fd2e111jq13me73.cloudfront.net%2Fsites%2Fdefault%2Ffiles%2Fstyles%2Fshare_link_image_large%2Fpublic%2Fproduct-images%2Fcsm-website%2Fgoogle-hangouts.jpg%3Fitok%3DzXGdUTfZ" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fd2e111jq13me73.cloudfront.net%2Fsites%2Fdefault%2Ffiles%2Fstyles%2Fshare_link_image_large%2Fpublic%2Fproduct-images%2Fcsm-website%2Fgoogle-hangouts.jpg%3Fitok%3DzXGdUTfZ" alt="Image result for Google Hangouts" width="800" height="400"&gt;&lt;/a&gt;(image was copied from &lt;a href="https://d2e111jq13me73.cloudfront.net/sites/default/files/styles/share_link_image_large/public/product-images/csm-website/google-hangouts.jpg?itok=zXGdUTfZ" rel="noopener noreferrer"&gt;here&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;If you have a google account (gmail, etc.) then you’re already setup to use Google Hangouts.&lt;/p&gt;

&lt;p&gt;Hangouts is a great feature because its already built in to Google products, and there are associated apps for your phone, tablet, etc.&lt;/p&gt;

&lt;p&gt;To get started, just go to Chrome and add the &lt;a href="https://chrome.google.com/webstore/detail/google-hangouts/nckgahadagoaajjgafhacjanaoiihapd?hl=en" rel="noopener noreferrer"&gt;Hangouts extension&lt;/a&gt;. You can also just pull up Gmail on your browser and you’ll see it already integrated into the lefthand navigation.&lt;/p&gt;

&lt;p&gt;Then you can directly message any of your friends that have a Google Email.&lt;/p&gt;

&lt;p&gt;Google Hangouts has a lot of cool options that are absolutely free:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can have group chats with up to 150 people&lt;/li&gt;
&lt;li&gt;You can share photos, videos, GIFs, etc.&lt;/li&gt;
&lt;li&gt;Record any conversation by turning on conversation history&lt;/li&gt;
&lt;li&gt;Connect it to Google voice for even more features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more info on how to get started, I recommend checking out these pages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://support.google.com/hangouts/answer/2944865?hl=en&amp;amp;ref_topic=6386410" rel="noopener noreferrer"&gt;Support Page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chrome.google.com/webstore/detail/google-hangouts/nckgahadagoaajjgafhacjanaoiihapd?hl=en" rel="noopener noreferrer"&gt;Chrome Extension&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  BlueJeans
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3czanvbgiewdet3vakih.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3czanvbgiewdet3vakih.png" alt="Image result for bluejeans video conference" width="300" height="300"&gt;&lt;/a&gt;(image was copied &lt;a href="https://fitsmallbusiness.com/wp-content/uploads/2018/11/BlueJeans-icon.png" rel="noopener noreferrer"&gt;from here&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;I was relatively new to BlueJeans until I used it recently. BlueJeans provides a dedicated desktop app as well as a web interface for meetings.&lt;/p&gt;

&lt;p&gt;Bluejeans is a paid application that offers several collaboration features including screen and application sharing.&lt;/p&gt;

&lt;p&gt;BlueJeans doesn’t have a free option (other than free trial offers). However, with the subscription you do get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;unlimited meeting time lengths&lt;/li&gt;
&lt;li&gt;unlimited 1 to 1 meetings&lt;/li&gt;
&lt;li&gt;unlimited group meetings&lt;/li&gt;
&lt;li&gt;Most of the same features as the other products&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;BlueJeans also has integration offerings with Microsoft Teams to use in your professional workspace. You can connect conference rooms to Microsoft teams using the gateway product at &lt;a href="https://www.bluejeans.com/products/integrations/microsoft-gateway" rel="noopener noreferrer"&gt;https://www.bluejeans.com/products/integrations/microsoft-gateway&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To get started with Bluejeans, please check out their plans site at &lt;a href="https://store.bluejeans.com/" rel="noopener noreferrer"&gt;https://store.bluejeans.com/&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Microsoft Teams
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F93s4q6i5rlhf2jdg1f9r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F93s4q6i5rlhf2jdg1f9r.png" alt="Image result for Microsoft Teams" width="800" height="768"&gt;&lt;/a&gt;(image was copied from &lt;a href="https://www.pngitem.com/pimgs/m/34-340278_microsoft-teams-logo-hd-png-download.png" rel="noopener noreferrer"&gt;here&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;If you’re a big user of &lt;a href="https://www.office.com/" rel="noopener noreferrer"&gt;Office 365&lt;/a&gt;, then Microsoft Teams would be a natural product for you.&lt;/p&gt;

&lt;p&gt;Microsoft Teams offers most of the same type of services that the other products I’ve mentioned cover. You can share screens, files and do general collaboration.&lt;/p&gt;

&lt;p&gt;Microsoft Teams has both free and paid versions. The advantage of using Microsoft Teams is if you or your team is already integrated with Microsoft products.&lt;/p&gt;

&lt;p&gt;Microsoft Teams offers a web interface and desktop applications.&lt;/p&gt;

&lt;p&gt;To get started with Microsoft Teams I recommend you check out the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://products.office.com/en-us/microsoft-teams/group-chat-software" rel="noopener noreferrer"&gt;Product Page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://products.office.com/en-us/microsoft-teams/compare-microsoft-teams-options" rel="noopener noreferrer"&gt;Pricing Plans&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;I hope the information I’ve provided here gives you a starting point for knowing which Video Conferencing software to use. There are a lot of options here and beyond the list I’ve provided. The biggest thing is to determine what solution works for your project or team. It also helps to consider what software applications that you want to share or collaborate with.&lt;/p&gt;

&lt;p&gt;Thanks for reading my article! Follow me on twitter at &lt;a href="https://twitter.com/andrewevans0102?lang=en" rel="noopener noreferrer"&gt;@AndrewEvans0102&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>collaboration</category>
      <category>remote</category>
      <category>videoconferencing</category>
    </item>
    <item>
      <title>Getting Started with RxJS</title>
      <dc:creator>Andrew Evans</dc:creator>
      <pubDate>Wed, 12 Feb 2020 17:33:05 +0000</pubDate>
      <link>https://forem.com/andrewevans0102/getting-started-with-rxjs-2dl0</link>
      <guid>https://forem.com/andrewevans0102/getting-started-with-rxjs-2dl0</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fit5bp8hqnwcopi5rhl62.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fit5bp8hqnwcopi5rhl62.jpg" alt="Creek, Falls, Flow, Flowing, Green, Landscape, Moss" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re just starting or a seasoned JavaScript developer, chances are you’ve heard of RxJS.&lt;/p&gt;

&lt;p&gt;RxJS is one of the most popular JavaScript Libraries that exists today. This post is going to cover a basic walkthrough of what it is, and how you can use it in your applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  History
&lt;/h2&gt;

&lt;p&gt;So before I begin, it helps to get a understanding of the history behind RxJS.&lt;/p&gt;

&lt;p&gt;It all started with &lt;a href="http://reactivex.io/" rel="noopener noreferrer"&gt;Reactive Extensions&lt;/a&gt; (or ReactiveX). ReactiveX was a concept that was originally invented by&lt;a href="https://en.wikipedia.org/wiki/Erik_Meijer_(computer_scientist)" rel="noopener noreferrer"&gt;Erik Meijer&lt;/a&gt;. It was an implementation of the &lt;a href="https://en.wikipedia.org/wiki/Observer_pattern" rel="noopener noreferrer"&gt;Observer Pattern&lt;/a&gt;. After it was developed, subsequent programming Libraries were developed around the major languages like .NET and JavaScript.&lt;/p&gt;

&lt;p&gt;RxJS is the implementation of Reactive Extensions for JavaScript. The &lt;a href="https://github.com/ReactiveX/rxjs" rel="noopener noreferrer"&gt;RxJS project&lt;/a&gt; was originally started by &lt;a href="https://github.com/mattpodwysocki" rel="noopener noreferrer"&gt;Matthew Podwysocki&lt;/a&gt; and others as independent open source project. Starting around the time of RxJS version 5, &lt;a href="https://github.com/benlesh" rel="noopener noreferrer"&gt;Ben Lesh&lt;/a&gt; and others improved the project to be more of what it is today.&lt;/p&gt;

&lt;p&gt;The RxJS Library implements both the &lt;a href="https://en.wikipedia.org/wiki/Observer_pattern" rel="noopener noreferrer"&gt;Observer Pattern&lt;/a&gt; and the &lt;a href="https://en.wikipedia.org/wiki/Iterator_pattern" rel="noopener noreferrer"&gt;Iterator Pattern&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The RxJS library also uses &lt;a href="https://en.wikipedia.org/wiki/Functional_programming" rel="noopener noreferrer"&gt;Functional Programming&lt;/a&gt; to implement operators and functions for managing sequences of events (streams). For a great intro to Functional Prgoramming I highly recommend watching Russ Olsen’s video at GOTO 2018.&lt;/p&gt;

&lt;h2&gt;
  
  
  Imperative vs Declarative
&lt;/h2&gt;

&lt;p&gt;When you hear people discuss RxJS, you commonly will hear them referring to &lt;strong&gt;imperative&lt;/strong&gt; and &lt;strong&gt;declarative&lt;/strong&gt; coding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Imperative&lt;/strong&gt; refers to code that you write in a specific way. This is code that you have manually piped the control flow similar to the way &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise" rel="noopener noreferrer"&gt;Promises&lt;/a&gt; work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Declarative&lt;/strong&gt; refers to using declared functions to perform actions. Here you rely upon “pure” functions that can define an event flow. With RxJS you see this in the form of &lt;a href="https://rxjs.dev/guide/observable" rel="noopener noreferrer"&gt;observables&lt;/a&gt; and &lt;a href="https://rxjs.dev/guide/operators" rel="noopener noreferrer"&gt;operators&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;These definitions will be more apparent to you later on in this post, but its good to introduce them here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Observables
&lt;/h2&gt;

&lt;p&gt;When explaining RxJS it typically is easiest to do by showing code first.&lt;/p&gt;

&lt;p&gt;Most people are typically familiar with a Promise implemented as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh6.googleusercontent.com%2FO23uW6AacWlq_xwOGN9m9g1e70GfPNo3LeyT4MzFM1i-bIw3O-olEv6o2x2bFbV4jkfxJtsfTx0fek_GqaecHha6bFNdVJgN2SAZrlhe5_gQ3S8Eulv77fOsumk1wpSPXjirEJQ" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh6.googleusercontent.com%2FO23uW6AacWlq_xwOGN9m9g1e70GfPNo3LeyT4MzFM1i-bIw3O-olEv6o2x2bFbV4jkfxJtsfTx0fek_GqaecHha6bFNdVJgN2SAZrlhe5_gQ3S8Eulv77fOsumk1wpSPXjirEJQ" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nothing super exciting here, just using the standard “resolve/reject” syntax. After the promise completes the output message is written to the console.&lt;/p&gt;

&lt;p&gt;Now compare that to this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2FrwHgKU_1h_ljjB9TOgJa9LH154l7gtZhetEtAqOmlXbOA7eCKMnhg828pado-ThPncatYMVYj36vV7FkDg-X1NI52CdvDcFqNh6_i9vUEJw-XSryqEZbYcwX-QT2-oFY_G0vMqw" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2FrwHgKU_1h_ljjB9TOgJa9LH154l7gtZhetEtAqOmlXbOA7eCKMnhg828pado-ThPncatYMVYj36vV7FkDg-X1NI52CdvDcFqNh6_i9vUEJw-XSryqEZbYcwX-QT2-oFY_G0vMqw" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Woah! What’s that? Well that’s RxJS! If you notice, the declarative practice is being used as the observable is first defined, and then the different hooks in the observer are used with &lt;code&gt;next&lt;/code&gt; , &lt;code&gt;error&lt;/code&gt;, and &lt;code&gt;complete&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I’m going to come back to this example later in this article, but just wanted to introduce it first.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does RxJS work?
&lt;/h2&gt;

&lt;p&gt;So to start with RxJS, it helps to first have a few definitions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Observable&lt;/strong&gt; = a defined stream of events&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt; = represents the actual &lt;strong&gt;execution flow&lt;/strong&gt; of events (initiating a subscription basically “turns on” the execution)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operators&lt;/strong&gt; = are “pure” functions that can invoke flows with subscriptions. These have different forms that can either create a stream or reproduce a stream in a pipeable flow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subject&lt;/strong&gt; = an event emitter that can be used for multicasting. These are special and used so that you can essentially inject emitters in your programs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schedulers&lt;/strong&gt; = these help with concurrency and are really a more advanced RxJS topic. I’m just including it here for completeness.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;For more info and examples please refer to the &lt;a href="https://rxjs.dev/guide/overview" rel="noopener noreferrer"&gt;official RxJS Getting Started Guide here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So with that vocabulary introduced, now we can formally discuss the example I introduced earlier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Observables (again)
&lt;/h2&gt;

&lt;p&gt;So lets go back to the code that I showed before:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2FrwHgKU_1h_ljjB9TOgJa9LH154l7gtZhetEtAqOmlXbOA7eCKMnhg828pado-ThPncatYMVYj36vV7FkDg-X1NI52CdvDcFqNh6_i9vUEJw-XSryqEZbYcwX-QT2-oFY_G0vMqw" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2FrwHgKU_1h_ljjB9TOgJa9LH154l7gtZhetEtAqOmlXbOA7eCKMnhg828pado-ThPncatYMVYj36vV7FkDg-X1NI52CdvDcFqNh6_i9vUEJw-XSryqEZbYcwX-QT2-oFY_G0vMqw" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a great example because it shows you an implemented Observable.&lt;/p&gt;

&lt;p&gt;If you notice first you define the Observable with &lt;code&gt;next&lt;/code&gt; and &lt;code&gt;complete&lt;/code&gt;. Then when I start the execution flow with the &lt;code&gt;subscribe&lt;/code&gt; I include definitions for what to do with the execution flow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;next&lt;/strong&gt; = does a &lt;code&gt;console.log&lt;/code&gt; of what is returned from the stream&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;error&lt;/strong&gt; = does a &lt;code&gt;console.log&lt;/code&gt; if an error occurs in the stream&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;complete&lt;/strong&gt; = writes &lt;code&gt;done&lt;/code&gt; to the console when execution is finished&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is one way to define an observable directly. Each observer has the three hooks of &lt;code&gt;next&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;, and &lt;code&gt;complete&lt;/code&gt; that you can use to define execution behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Operators
&lt;/h2&gt;

&lt;p&gt;Obserables are great, but RxJS also offers &lt;strong&gt;operators&lt;/strong&gt; which make defining observables much easier.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;operators&lt;/strong&gt; there are two types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;creation operators&lt;/strong&gt; = generated observables with predefined behavior&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pipeable operators&lt;/strong&gt; = observables that return other observables using the syntax “.pipe”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s a &lt;strong&gt;creation operator&lt;/strong&gt; in action:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh6.googleusercontent.com%2FgfVCsFGnnmOkvPfAmm05QBc3BVOnQbSNlgbCGS-iw4pHauUTduiT2Lj-lTY6sbJIYt_rEcwyHoLit7PC5bifg9P72voABBrF3Gk2KOpMpjmITdr_qSrj7rtMOMfktfcDjsXvkl4" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh6.googleusercontent.com%2FgfVCsFGnnmOkvPfAmm05QBc3BVOnQbSNlgbCGS-iw4pHauUTduiT2Lj-lTY6sbJIYt_rEcwyHoLit7PC5bifg9P72voABBrF3Gk2KOpMpjmITdr_qSrj7rtMOMfktfcDjsXvkl4" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we are using the &lt;code&gt;of&lt;/code&gt; operator to emit values of &lt;code&gt;10&lt;/code&gt;, &lt;code&gt;20&lt;/code&gt;, and &lt;code&gt;30&lt;/code&gt; in a sequence. This is super basic, but gives you an idea of how you could use this to emit a set of values in a stream without needing to manually define the observer hooks.&lt;/p&gt;

&lt;p&gt;Here’s a &lt;strong&gt;pipeable operator&lt;/strong&gt; in action:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh3.googleusercontent.com%2FMU0YdejQ1ZNmUkxJLKWBjyA-A-GQKNsDWibxyUnNfqhMQUNUnsh9ynFU3W3Ki75cW1ReCXSoILjMoWmyFbSfiq1fAp6oHyuC5VRv4XpgR3o1KwwWLzLtaDmYOjsxTG_4bLpCd8c" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh3.googleusercontent.com%2FMU0YdejQ1ZNmUkxJLKWBjyA-A-GQKNsDWibxyUnNfqhMQUNUnsh9ynFU3W3Ki75cW1ReCXSoILjMoWmyFbSfiq1fAp6oHyuC5VRv4XpgR3o1KwwWLzLtaDmYOjsxTG_4bLpCd8c" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So here, its a little more complicated, but I think you can figure it out.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We’re using the &lt;strong&gt;creation operator&lt;/strong&gt; &lt;code&gt;of&lt;/code&gt; that I referenced before to generate a stream of values &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt;, &lt;code&gt;c&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;Next we take the output from &lt;code&gt;of&lt;/code&gt; into the &lt;strong&gt;pipeable operator&lt;/strong&gt; &lt;code&gt;mergeMap&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Then we’re letting &lt;code&gt;mergeMap&lt;/code&gt; create a new observable and pipe it into &lt;code&gt;interval&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Then &lt;code&gt;interval&lt;/code&gt; takes the output and &lt;code&gt;console.log&lt;/code&gt; each value after a 1 second delay&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So basically this creates a flow with the &lt;code&gt;pipeable&lt;/code&gt; operators. The original source observable is used to recreate a new observable with added logic.&lt;/p&gt;

&lt;p&gt;An easier way to think of this is as a &lt;code&gt;stream&lt;/code&gt; is being defined here. Each pipe that is used with the stream adds value.&lt;/p&gt;

&lt;p&gt;A more literal way to think of &lt;strong&gt;pipeable operators&lt;/strong&gt; is as water flows through a set of pipes. Each pipe adds value to the water until it exits the flow.&lt;/p&gt;

&lt;p&gt;Visually you can see this flow in the following diagram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fatevans85.files.wordpress.com%2F2020%2F02%2Fscreen-shot-2020-02-12-at-12.31.32-pm.png%3Fw%3D1024" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fatevans85.files.wordpress.com%2F2020%2F02%2Fscreen-shot-2020-02-12-at-12.31.32-pm.png%3Fw%3D1024" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For a better visual of flows with RxJS, many people rely on Marble Diagrams. They’re a little daunting at first, but if you learn how to read them with the RxJS Guide they really help. I recommend checking out the &lt;a href="https://rxjs.dev/guide/operators" rel="noopener noreferrer"&gt;RxJS guide at the bottom of the operators page here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Subscriptions and Memory Leaks
&lt;/h2&gt;

&lt;p&gt;So one big challenge that developers run into with RxJS is memory leaks with subscriptions.&lt;/p&gt;

&lt;p&gt;Memory leaks are when you’ve forgotten to “unsubscribe” from a stream, and the process continues to run eating up your memory. Memory leaks can quickly eat up your browsers memory and slow down your application.&lt;/p&gt;

&lt;p&gt;The best solution is to always make sure you have a &lt;code&gt;.unsubscribe&lt;/code&gt; for your observables. You also can rely on prebuilt mechansims in frameworks like Angular’s &lt;code&gt;async&lt;/code&gt; pipe.&lt;/p&gt;

&lt;p&gt;Here’s some code that creates a memory leak:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh6.googleusercontent.com%2FIDp1L1Z9r7ba5_izPJywKy0AuToMt26v0O8RCeXCFvLPznb5-vvXjLXs1A56sx1D6LOMu0wpjs7OZIRae_Bs4sYXTKqDcot6zpiIfHk-quBVHJhmSmvXn7EMntyLYU6OBUqt-6M" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh6.googleusercontent.com%2FIDp1L1Z9r7ba5_izPJywKy0AuToMt26v0O8RCeXCFvLPznb5-vvXjLXs1A56sx1D6LOMu0wpjs7OZIRae_Bs4sYXTKqDcot6zpiIfHk-quBVHJhmSmvXn7EMntyLYU6OBUqt-6M" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This code can easily be fixed by adding a &lt;code&gt;setTimeout&lt;/code&gt; that unsubscribes from the stream after a set interval of time like so:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh5.googleusercontent.com%2FzYlpV04MkPvFmI8oEbcciqQlQ3t3L9YPM5XtyzdxaP44rzZHzJqK7c3z77eTHuMKq1uqD6ZuUW-AfrqXc0bFpuycUeN1urbmcQ8b0frk-j5JcqXjEm2R_hmu0qOGId-0krkUyrQ" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh5.googleusercontent.com%2FzYlpV04MkPvFmI8oEbcciqQlQ3t3L9YPM5XtyzdxaP44rzZHzJqK7c3z77eTHuMKq1uqD6ZuUW-AfrqXc0bFpuycUeN1urbmcQ8b0frk-j5JcqXjEm2R_hmu0qOGId-0krkUyrQ" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I actually contributed an article on the RxJS DEVTO blog on this topic &lt;a href="https://dev.to/rxjs/common-approaches-to-handling-subscriptions-1nk7"&gt;titled “Common Approaches to Handling Subscriptions” here&lt;/a&gt;. I highly recommend checking out my post when you have some time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Topics
&lt;/h2&gt;

&lt;p&gt;So far we’ve just covered some basic execution flows. If you combine RxJS operators, you can define some pretty complicated work in just a small amount of code.&lt;/p&gt;

&lt;p&gt;The challenge happens when you create a group of Observables from a single Observable. This is called a &lt;strong&gt;Higher Order Observable&lt;/strong&gt;. RxJS has operators that help you with flattening these situations to include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://rxjs.dev/api/operators/concatAll" rel="noopener noreferrer"&gt;concatAll()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rxjs.dev/api/operators/mergeAll" rel="noopener noreferrer"&gt;mergeAll()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rxjs.dev/api/operators/switchAll" rel="noopener noreferrer"&gt;switchAll()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rxjs.dev/api/operators/exhaust" rel="noopener noreferrer"&gt;exhaust()&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m not going to dive into a deep example of Higher Order Observables here because I think it goes beyond the scope of an introductory article. I do, however, highly recommend checking out the &lt;a href="https://rxjs.dev/guide/operators" rel="noopener noreferrer"&gt;RxJS Guide on operators that discusses this in more detail&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I also cover a more advanced topic in my &lt;a href="https://dev.to/rxjs/from-promises-to-observables-4bdk"&gt;RxJS DEVTO blog site post “From Promises to Observables” here&lt;/a&gt;. In that case I walkthrough setting up a &lt;code&gt;scan&lt;/code&gt; operator to combine several HTTP calls.&lt;/p&gt;

&lt;p&gt;I recommend reading posts on the &lt;a href="https://dev.to/rxjs"&gt;RxJS DEVTO blog site&lt;/a&gt; for more advanced Observable cases and discussion as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;p&gt;My discussion in this post has just covered the surface of what you can do with RxJS. There are also lots of great materials and videos available on line that provide in depth walkthroughs and examples.&lt;/p&gt;

&lt;p&gt;I recommend checking out these videos as a good place to start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://youtu.be/0if71HOyVjY" rel="noopener noreferrer"&gt;GOTO 2018 – Functional Programming in 40 Minutes – Russ Olsen&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://realtalkjavascript.simplecast.com/episodes/39f4a2e2-1fe70e46" rel="noopener noreferrer"&gt;RealTalk JavaScript Episode 31: RxJS Wizardry with Ben Lesh&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=K7AvXUNB2X8&amp;amp;t=1292s" rel="noopener noreferrer"&gt;Ng-Cruise – RxJS By Example with Ben Lesh&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=m40cF91F8_A" rel="noopener noreferrer"&gt;Creating an Observable from Scratch (live-coding session) – Ben Lesh&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing Remarks
&lt;/h2&gt;

&lt;p&gt;I hope this post has helped you with learning RxJS. In this post I walked through what RxJS is, and ways you can use it in your programs.&lt;/p&gt;

&lt;p&gt;Thank you for reading this post! Follow me on Twitter at &lt;a href="https://twitter.com/andrewevans0102" rel="noopener noreferrer"&gt;@AndrewEvans0102&lt;/a&gt;, and check out what I’m working on at &lt;a href="https://www.andrewevans.dev" rel="noopener noreferrer"&gt;andrewevans.dev&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rxjs</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Angular, Jest, Firebase, and ReyRey!</title>
      <dc:creator>Andrew Evans</dc:creator>
      <pubDate>Mon, 06 Jan 2020 17:19:39 +0000</pubDate>
      <link>https://forem.com/andrewevans0102/angular-jest-firebase-and-reyrey-5eon</link>
      <guid>https://forem.com/andrewevans0102/angular-jest-firebase-and-reyrey-5eon</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fatevans85.files.wordpress.com%2F2020%2F01%2Frey2-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fatevans85.files.wordpress.com%2F2020%2F01%2Frey2-1.png" alt="ReyRey" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;Rey after her latest restaurant adventure to our local Starbucks.
  &lt;/p&gt;

&lt;p&gt;I’ve written several posts in the past using Firebase and the &lt;a href="https://github.com/angular/angularfire" rel="noopener noreferrer"&gt;AngularFire2&lt;/a&gt; library. The AngularFire2 library makes using and integrating Firebase with your Angular applications super fun and easy.&lt;/p&gt;

&lt;p&gt;AngularFire2 also enables you to build JAMStack applications which only require a frontend and calls to the various Firebase services (Auth, Database, etc.). After following the docs on &lt;a href="https://github.com/angular/angularfire/blob/master/docs/install-and-setup.md" rel="noopener noreferrer"&gt;the AngularFire2 README&lt;/a&gt;, you can get up and running fairly easily. From there its just a matter of injecting the different services into your Angular Components.&lt;/p&gt;

&lt;p&gt;I recently built an Angular application that uses AngularFire2. The application also uses &lt;a href="https://jestjs.io/" rel="noopener noreferrer"&gt;Jest&lt;/a&gt; for unit testing. I learned some in the process of building it and wanted to share for future reference.&lt;/p&gt;

&lt;p&gt;This post is going to cover my app and some basics about setting up Jest. I’m not going to cover the initial setup of AngularFire2 since their GitHub repo covers it. I’m also not going to go over a lot about integrating Jest with Angular, except to say that I’m using an &lt;a href="https://angular.io/guide/cli-builder" rel="noopener noreferrer"&gt;Angular Builder&lt;/a&gt;for Jest in lieu of Karma. Builders are great since they let you leverage the Angular CLI. I’ll cover briefly more on those and using Jest in the first section.&lt;/p&gt;

&lt;h2&gt;
  
  
  ReyRey’s Restaurants
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fatevans85.files.wordpress.com%2F2020%2F01%2Fscreen-shot-2020-01-07-at-4.31.47-am.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fatevans85.files.wordpress.com%2F2020%2F01%2Fscreen-shot-2020-01-07-at-4.31.47-am.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The app that I’m going to cover is called “ReyRey’s Restaurants.” You can reach it by going to &lt;a href="https://www.reyreysrestaurants.com" rel="noopener noreferrer"&gt;https://www.reyreysrestaurants.com&lt;/a&gt;. The application is a fun way to keep track of restaurants you visit in your city. The project is built and hosted with &lt;a href="https://firebase.google.com/" rel="noopener noreferrer"&gt;Firebase&lt;/a&gt;, and built with&lt;a href="https://github.com/angular/angularfire" rel="noopener noreferrer"&gt;AngularFire2&lt;/a&gt; to connect to the authentication and database services. I made it open source and you can &lt;a href="https://github.com/andrewevans0102/reyreys-restaurants" rel="noopener noreferrer"&gt;check out the source code on GitHub here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also, the reason I built the application was to have a fun way to track restaurants in my city and incorporate my cat (Rey) into one of my projects.0. I already have &lt;a href="https://github.com/andrewevans0102/chessie-choochoo" rel="noopener noreferrer"&gt;Chessie Choochoo&lt;/a&gt; for my other cat (Chestnut) so I didn’t want to leave out Rey (checkout &lt;a href="https://www.chessiechoochoo.com" rel="noopener noreferrer"&gt;https://www.chessiechoochoo.com&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;I setup some docs so you can &lt;a href="https://andrewevans0102.github.io/reyreys-restaurants/" rel="noopener noreferrer"&gt;easily see how to use the app here&lt;/a&gt;. The basic premise is that you create an account, then add restaurants to a “wanna go” section. When you visit your restaurant you can “promote” it to “been there” and add a review with comments and stars etc. here are some screenshots:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fatevans85.files.wordpress.com%2F2020%2F01%2Fcreate2-collage.jpg%3Fw%3D897" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fatevans85.files.wordpress.com%2F2020%2F01%2Fcreate2-collage.jpg%3Fw%3D897" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;As I mentioned in the start, the two big things with this project were &lt;a href="https://github.com/angular/angularfire" rel="noopener noreferrer"&gt;AngularFire2&lt;/a&gt; and &lt;a href="https://jestjs.io/en/" rel="noopener noreferrer"&gt;Jest&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’m not going to go into how to setup AngularFire2 with your project since the README in their repo pretty much covers it. I will, however, point you to my post on &lt;a href="https://indepth.dev/how-the-angularfire-library-makes-firebase-feel-like-magic/#angularfire--1" rel="noopener noreferrer"&gt;How the AngularFire Library makes Firebase feel like Magic&lt;/a&gt; as that has a good set of instructions to get you started.&lt;/p&gt;

&lt;p&gt;For setting up Jest with your project, there are a few ways to do this. I found using the&lt;a href="https://github.com/just-jeb/angular-builders/tree/master/packages/jest" rel="noopener noreferrer"&gt;Jest Builder here was the easiest option for me&lt;/a&gt;. Other than the instructions on the README, I also did the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;created a &lt;a href="https://github.com/andrewevans0102/reyreys-restaurants/blob/master/jest.config.js" rel="noopener noreferrer"&gt;jest.config.js&lt;/a&gt; file based on the recommended example from the builder Repo&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/andrewevans0102/reyreys-restaurants/commit/73cd47f9ce9ad6b26efdcaa17a7f37833baccc48#diff-2d0cd5d10b9604941c38c6aac608178a" rel="noopener noreferrer"&gt;added a “–runInBand” option for the Jest CLI&lt;/a&gt;to resolve memory errors I was seeing with my CircleCI build&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The cool part about using a builder was that I was able to leverage the existing Angular CLI to do this work. So anytime I called “ng test” it would invoke the Jest test runner rather than the Karma runner that is normally the default.&lt;/p&gt;

&lt;p&gt;After playing with it some I have to say I really liked Jest because of the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The error messages and warnings were easy to understand&lt;/li&gt;
&lt;li&gt;The test runner gives you more fine grained options&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m not really going to go into a lot about Jest because several other folks have covered this very well. I recommend reviewing the post &lt;a href="https://medium.com/angular-in-depth/search?q=jest" rel="noopener noreferrer"&gt;Angular CLI: “ng test” with Jest in 3 minutes (v2)&lt;/a&gt;. Also (event hough the article doesn’t use builders) I recommend checking out the article &lt;a href="https://medium.com/angular-in-depth/integrate-jest-into-an-angular-application-and-library-163b01d977ce" rel="noopener noreferrer"&gt;Integrate Jest into an Angular application and library&lt;/a&gt; for more on Jest with Angular. Finally, the &lt;a href="https://jestjs.io/docs/en/getting-started" rel="noopener noreferrer"&gt;Jest Getting Started Docs&lt;/a&gt; are a great place to go for examples and much more in depth information.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Angularfire2 with Jest
&lt;/h2&gt;

&lt;p&gt;Normally, unit testing of libraries with different services was pretty straightforward. You mock the dependencies that you need to inject and use the various hooks (beforeEach, afterEach etc.) to handle the data you’re testing.&lt;/p&gt;

&lt;p&gt;With AngularFire2 I had a number of issues trying to mock the different libraries because of the different methods that I needed to handle for my components etc. This wasn’t documented as much as I had hoped, and required a fairly extensive amount of googling. Fortunately, I found the &lt;a href="https://github.com/angular/angularfire/issues/18" rel="noopener noreferrer"&gt;GitHub issue here&lt;/a&gt; that discusses adding docs on testing to the project repo. Within this GitHub issue &lt;a href="https://github.com/angular/angularfire/issues/18#issuecomment-351670746" rel="noopener noreferrer"&gt;this response had a great example&lt;/a&gt; that helped me to learn how to do this for my project.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;As a small disclaimer, before I go into my tests I want to note that there are still a lot of tests that could be added. As of this writing, time constraints have limited how many I can add. I’ve built out a successful set of tests for the service classes in my project. The setup I used for these tests can be used for the components that rely on them as well.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I wrote a set of service classes that pull out the AngularFire2 services into their own classes. This made it easier because then I had more flexibility with naming and how I wanted to use AngularFire2.&lt;/p&gt;

&lt;p&gt;The basic process to test these services is to create stub, and mock the values for the AngularFire2 library methods. These mock values then actually mock the real values that would be returned from the Firebase service methods.&lt;/p&gt;

&lt;p&gt;For the &lt;a href="https://github.com/andrewevans0102/reyreys-restaurants/blob/master/src/app/services/authentication/authentication.service.ts" rel="noopener noreferrer"&gt;Authentication Service&lt;/a&gt; I have the following setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;credentialsMock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abc@123.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;password&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;userMock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ABC123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;credentialsMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createUserMock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ABC123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;credentialsMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fakeAuthState&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;BehaviorSubject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fakeSignInHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;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;fakeAuthState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userMock&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&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;userMock&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;fakeCreateUserHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;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;fakeAuthState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;createUserMock&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&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;createUserMock&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;fakeSignOutHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;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;fakeAuthState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&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="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;angularFireAuthStub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;authState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fakeAuthState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;createUserWithEmailAndPassword&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nf"&gt;fakeCreateUserHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;signInWithEmailAndPassword&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nf"&gt;fakeSignInHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;signOut&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="nf"&gt;fakeSignOutHandler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in my actual test &lt;code&gt;describe&lt;/code&gt; block I reference the &lt;code&gt;angularFireAuthStub&lt;/code&gt; value here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AuthenticationService&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="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AuthenticationService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;afAuth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AngularFireAuth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;beforeEach&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;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configureTestingModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AngularFireAuth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;angularFireAuthStub&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&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;AuthenticationService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;afAuth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&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;AngularFireAuth&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;afterEach&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;fakeAuthState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in the tests themselves I just call my service methods and check for the mock and stub responses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should call the create user with email and password successfully&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createUserWithEmailAndPassword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;credentialsMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;credentialsMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&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;response&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;createUserMock&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;uid&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;Once I had the Authentication Service up and running I next built out the tests for the &lt;a href="https://github.com/andrewevans0102/reyreys-restaurants/blob/master/src/app/services/database/database.service.ts" rel="noopener noreferrer"&gt;Database Service&lt;/a&gt;. The setup was similar to the Authentication Service and had the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DatabaseService&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;savedValues&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ABC123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;first&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;last&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abc@123.com&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;wgRestaurant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;WgRestaurant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1234&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abc123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;description&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;recorded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1234&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;btRestaurant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BtRestaurant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1234&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;5678&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;restaurant name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;restaurant description&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;restaurant location&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;restaurant link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;stars&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;review&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;restaurant review&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;recorded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1234&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;fakeAddValueHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;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;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&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;savedValues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deleteAddedValueHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;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;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&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;savedValues&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]));&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firestoreStub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;valueChanges&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BehaviorSubject&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&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="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;d&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fakeAddValueHandler&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="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;createId&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;return&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="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1234567890&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;doc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;idFirst&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;idSecond&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;valueChanges&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BehaviorSubject&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&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="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;d&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fakeAddValueHandler&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="na"&gt;delete&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="nf"&gt;deleteAddedValueHandler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you notice I’m just generically using an array whenever values are saved to the Cloud Firestore database. This obviously be customized for more fine grained testing. I was just really concerned with basic calling of the different methods here so I left it this way.&lt;/p&gt;

&lt;p&gt;I used a &lt;code&gt;beforeEach&lt;/code&gt; and &lt;code&gt;afterEach&lt;/code&gt; to setup the tests as you see here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;beforeEach&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;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configureTestingModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AngularFirestore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;firestoreStub&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&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;DatabaseService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// clear out any saved values&lt;/span&gt;
&lt;span class="nf"&gt;afterEach&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;savedValues&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, finally when the tests are actually called you just call the service methods and check for the stub and mock values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should call add user successfully&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addUser&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;savedValues&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="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I’ve had a lot of experience with Karma, so this was my first time really playing with Jest. Overall I found it very intuitive and it was fairly easy to work with. In particular I liked the warning and messages that the CLI gave me. They really helped to workout what I needed for configuration and building tests normally etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;So I hope you enjoyed this post and you learned something from it as well. I really enjoy using AngularFire2 with my projects because it makes it easy to integrate Firebase into Angular apps. It was also cool to use Jest for unit testing instead of Karma has I had always done before. My project here really just covers some basics and there is a lot more you can do with both AngularFire2 and Jest. Also I hope you check out &lt;a href="https://www.reyreysrestaurants.com/" rel="noopener noreferrer"&gt;ReyRey’s Restaurants&lt;/a&gt;, and possibly even use it as you’re checking out local restaurants!&lt;/p&gt;

&lt;p&gt;Thank you for reading! Follow me on Twitter at &lt;a href="https://twitter.com/andrewevans0102" rel="noopener noreferrer"&gt;@AndrewEvans0102&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>angular</category>
      <category>unittesting</category>
      <category>firebase</category>
    </item>
  </channel>
</rss>
