<?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: MartinJ</title>
    <description>The latest articles on DEV Community by MartinJ (@mjoycemilburn).</description>
    <link>https://dev.to/mjoycemilburn</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%2F609115%2Fc92679c4-dc71-4f59-8d49-9576b16b27b6.jpg</url>
      <title>DEV Community: MartinJ</title>
      <link>https://dev.to/mjoycemilburn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mjoycemilburn"/>
    <language>en</language>
    <item>
      <title>NgSysV2-10.2: PowerShell Scripting essentials</title>
      <dc:creator>MartinJ</dc:creator>
      <pubDate>Mon, 30 Mar 2026 17:18:08 +0000</pubDate>
      <link>https://dev.to/mjoycemilburn/ngsysv2-102-powershell-scripting-essentials-23cj</link>
      <guid>https://dev.to/mjoycemilburn/ngsysv2-102-powershell-scripting-essentials-23cj</guid>
      <description>&lt;p&gt;This post series is indexed at NgateSystems.com. You'll find a super-useful keyword search facility there too.&lt;/p&gt;

&lt;p&gt;Last reviewed: Apr'26&lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;The concept of "scripting" to automate PowerShell terminal sessions was introduced in &lt;a href="https://dev.to/mjoycemilburn/ngsysv2-43-automated-svelte-pre-render-builds-1618"&gt;Post 4.3&lt;/a&gt; without any attempt to describe the script language's syntax.&lt;/p&gt;

&lt;p&gt;This post still doesn't fully cover the subject, but here are the essentials, plus one or two "extras" I've found invaluable.&lt;/p&gt;

&lt;h4&gt;
  
  
  Variables, Arrays and Operators
&lt;/h4&gt;

&lt;p&gt;A variable in PowerShell is a named container that holds a value, such as a string, number, array or object. PowerShell variables are dynamically typed, meaning you don't need to declare a variable's data type when assigning a value; it is determined by the assigned value. But note that once assigned, types are strongly enforced.&lt;/p&gt;

&lt;p&gt;Variable names in PowerShell are introduced with a $ symbol. Variable names are not case sensitive, so, for instance, $MyVariable and $myvariable refer to the same variable.&lt;/p&gt;

&lt;h4&gt;
  
  
  Operators
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;Arithmetical&lt;/em&gt; — For mathematical calculations (+, -, &lt;code&gt;*&lt;/code&gt;, /, %)&lt;br&gt;
&lt;em&gt;Comparative&lt;/em&gt; — For comparing values (-eq, -ne, -gt, -lt, -ge, -le)&lt;br&gt;
&lt;em&gt;Logical&lt;/em&gt; — For combining conditions (-and, -or, -not)&lt;/p&gt;
&lt;h4&gt;
  
  
  Comments
&lt;/h4&gt;

&lt;p&gt;Introduce these with a # symbol,&lt;/p&gt;
&lt;h4&gt;
  
  
  Output
&lt;/h4&gt;

&lt;p&gt;You can write to the console with the following arrangement&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Write-Output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MyMessage&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# write this to the terminal&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can write to a file as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$LogPath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"c:/path-to-my-log-file"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$MyMessage&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;" .... "&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Add-Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$LogPath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Message&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Loops
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;For&lt;/em&gt; loop — Used when you know the exact number of iterations required. It’s commonly used for tasks such as incrementing counters or processing arrays.&lt;br&gt;
&lt;em&gt;While&lt;/em&gt; loop — Continues executing as long as a specified condition evaluates to True. It’s ideal for scenarios where the number of iterations depends on a dynamic condition.&lt;br&gt;
&lt;em&gt;ForEach&lt;/em&gt; loop — Designed for iterating through collections like arrays or output from commands.For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Initialise a counter&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-lt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;    
&lt;/span&gt;&lt;span class="n"&gt;Write-Output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Iteration: &lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;   
 &lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Conditionals
&lt;/h4&gt;

&lt;p&gt;Powershell's &lt;strong&gt;If-Else&lt;/strong&gt; syntax follows the following model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$num&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$num&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-gt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Write-Output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$num&lt;/span&gt;&lt;span class="s2"&gt; is greater than 5"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="kr"&gt;else&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Write-Output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$num&lt;/span&gt;&lt;span class="s2"&gt; is less than or equal to 5"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  "Null"
&lt;/h4&gt;

&lt;p&gt;In PowerShell,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$MyVariable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;means “This variable exists, but it contains no value”&lt;/p&gt;

&lt;p&gt;It is particularly useful because it evaluates to "false", along with :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$false → explicit boolean false&lt;/li&gt;
&lt;li&gt; $0 → numeric zero&lt;/li&gt;
&lt;li&gt;$"" → empty string&lt;/li&gt;
&lt;li&gt;$@() → empty array&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which is very handy because you can now do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$MyVariable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$null&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$MyVariable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Write-Output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"True"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# or - a handy shortcut - just say "True"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Write-Output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"False"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Functions
&lt;/h4&gt;

&lt;p&gt;PowerShell lets you define and reference shared blocks of code with the following model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Display-Greeting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$Count&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Write-Output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Name: &lt;/span&gt;&lt;span class="nv"&gt;$Name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Write-Output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Count: &lt;/span&gt;&lt;span class="nv"&gt;$Count&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="kr"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-le&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$Count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Write-Output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hello &lt;/span&gt;&lt;span class="nv"&gt;$Name&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Display-Greeting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Martin"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Options exist for the function to accept only valid parameter values - ask ChatGPT for details&lt;/p&gt;

&lt;h4&gt;
  
  
  Referencing a script with parameters
&lt;/h4&gt;

&lt;p&gt;A parent script can call a subordinate "child" script as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".\child.ps1"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Param1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Param2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;value2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Meanwhile, the child script might be configured as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Mandatory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$Param1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Mandatory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$Param2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  The "Try ... Catch" block
&lt;/h4&gt;

&lt;p&gt;Just as in JavaScript, you can capture exceptions and direct them to a "problem-resolution" block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;    
    &lt;/span&gt;&lt;span class="c"&gt;# Code that might throw an error   &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;    
    &lt;/span&gt;&lt;span class="n"&gt;Write-Output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"An error occurred: &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="kr"&gt;finally&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;    
    &lt;/span&gt;&lt;span class="n"&gt;Write-Output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"This code always runs, regardless of errors"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More usefully, you might consider opening an account with &lt;code&gt;pushover&lt;/code&gt; and configuring your script to send a notification to your mobile phone. Registration and configuration are extremely easy and, last time I checked, a lifetime account with &lt;code&gt;pushover&lt;/code&gt;costs just $5.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;    
    &lt;/span&gt;&lt;span class="n"&gt;curl.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;NUL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--form-string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"priority=1"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;--form-string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"token=aiu7yk ..obfuscated... 5uerqq6ix"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;--form-string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user=ueczz ..obfuscated... jrv54u22"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;--form-string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"message=Something has gone wrong with your nightly .. run"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;https://api.pushover.net/1/messages.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The -s -o NUL parameter bit above simply suppresses the display of the "response" from &lt;code&gt;curl.exe&lt;/code&gt;, which is no help at all when this is running in the Windows scheduler.&lt;/p&gt;

&lt;p&gt;But when adding a &lt;code&gt;try&lt;/code&gt; block, you need to know that PowerShell does not throw exceptions for many errors by default (especially for external commands). You can fix this either by adding explicit error-handling instructions to individual commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Some-Command&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ErrorAction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Stop&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or, more realistically, by setting global instructions with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="bp"&gt;$Error&lt;/span&gt;&lt;span class="n"&gt;ActionPreference&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Stop"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  The "pipeline"
&lt;/h4&gt;

&lt;p&gt;An advanced PS1 script will make extensive use of an arrangement that lets you "chain" commands together with a "pipe" symbol - | -  that passes the output from one command as an object that then provides input to the next. So, in&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-Process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Sort-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;CPU&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Get-Process&lt;/code&gt; → produces "process objects"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Sort-Object&lt;/code&gt; → receives those objects and sorts them by a property&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No string parsing, no fragile text handling — just structured data flowing through. PowerShell passes objects, not text, and binds them by property name or type. Here's another example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-Service&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Where-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Running'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Get-Service&lt;/code&gt; → outputs service objects&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Where-Object&lt;/code&gt; → filters them, selecting those with status "running"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most pipelines follow this shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Producer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;Filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Transform&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-Process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Where-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CPU&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-gt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;CPU&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Out-File&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;processes.txt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll use these constantly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Where-Object&lt;/code&gt; → filter&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Select-Object&lt;/code&gt; → pick properties&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Sort-Object&lt;/code&gt; → sort&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ForEach-Object&lt;/code&gt; → act on each item&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>beginners</category>
      <category>devops</category>
      <category>programming</category>
      <category>automation</category>
    </item>
    <item>
      <title>NgSysV2-5.2: An Introduction to Git</title>
      <dc:creator>MartinJ</dc:creator>
      <pubDate>Fri, 20 Feb 2026 10:34:46 +0000</pubDate>
      <link>https://dev.to/mjoycemilburn/ngsysv2-52-a-young-persons-introduction-to-git-237n</link>
      <guid>https://dev.to/mjoycemilburn/ngsysv2-52-a-young-persons-introduction-to-git-237n</guid>
      <description>&lt;p&gt;&lt;em&gt;This post series is indexed at &lt;a href="https://ngatesystems.com" rel="noopener noreferrer"&gt;NgateSystems.com&lt;/a&gt;. You'll find a super-useful keyword search facility there too.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Last reviewed: Feb'26&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Introduction
&lt;/h3&gt;

&lt;p&gt;In British slang, a Git is an abusive term for a sullen, contemptible person. As far as is known, Linus Torvalds never intended GIT to be an acronym, so one can only assume that the development of this world-leading version control software gave him a particularly rough time! &lt;/p&gt;

&lt;p&gt;Words of abuse may pass your own lips from time to time, when you are using Git, but this is an invaluable tool, one now central to modern IT practice. Git is a version control system designed to manage the entire history of a software project. What kept Linus going? Let's find out. &lt;/p&gt;

&lt;p&gt;When you're new to IT and are still busy creating your first VSCode project, the last thing on your mind will be turning that project into a Git repository (whatever that might be!). But with experience, you'll realise that managing source code is quite a challenge. You will make changes, find yourself in a mess and want everything to return to a stable state. Hmm. How are you going to arrange that?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/mjoycemilburn/ngsysv2-11-a-young-persons-guide-to-systems-development-in-the-age-of-ai-39pe"&gt;Post 1.1&lt;/a&gt; mentioned VSCode's "timeline" facility. This lets you view a time-stamped list of "saves" for a file and enables you to select any of them to replace the current version. This is fine in its own way, but has obvious limitations - for example, what if you wanted to reset a whole bunch of interrelated files to a particular point? Git, by contrast, is "timeline" on steroids (and then some!)&lt;/p&gt;

&lt;p&gt;Git's "repository" mechanism wraps a project's files up in a framework that tracks changes between "commit" points. A "commit" lets you "sign off" on a collection of file changes as a "recovery" point for your project. Then, if things go badly wrong, you can use Git to roll back the entire project to whatever commit point you choose. Git helps you answer "how did this software change, when did it change, and why?" A "commit" is more than a simple file change; it is a recorded step in a project's evolution. Each commit represents a meaningful change, such as fixing a bug, adding a feature, or improving behaviour. Over time, commits provide a structured history of the software's development. &lt;/p&gt;

&lt;p&gt;Git also provides a "push" feature that lets you create and maintain a copy of your local project on the web. With this in place, your carefully crafted project won't vanish into history when you leave your laptop on the bus! Typically, you would use this to create a secure backup "recovery" copy of your code when you deploy a new version of your webapp. In many workflow designs, a Git push triggers a deployment.&lt;/p&gt;

&lt;p&gt;But in describing the "remote repository" concept in this way, I'm grossly understating its significance. In the world of systems engineering, the remote repository is the point through which large teams of developers, each operating on a local copy of the project code, coordinate their work. Git software provides the means of imposing order on what would otherwise be a nightmarish struggle. When Git is in control and the remote repository reflects the source of the production system, developers always know exactly what code is running and how it came into being.&lt;/p&gt;

&lt;p&gt;A description of these "team" arrangements is well outside the scope of this post - the &lt;a href="https://git-scm.com/book/en/v2/Getting-Started-About-Version-Control" rel="noopener noreferrer"&gt;Pro Git book&lt;/a&gt; would be an &lt;strong&gt;excellent&lt;/strong&gt; place to take you to a higher level. For now, here are detailed instructions for the things you are most likely to want to do as a beginner:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Turn the code for an existing local project into a repository and create a commit point&lt;/li&gt;
&lt;li&gt;Recover to a commit point* Create a remote repo on the web and synchronise this with a local repo.&lt;/li&gt;
&lt;li&gt;Create a remote Git repo on the web and synchronise this with your local repo.&lt;/li&gt;
&lt;li&gt;Create "branches" on both your local and remote repositories to isolate your production code &lt;/li&gt;
&lt;li&gt;Forward a "hot-fix" on "main" into "dev" using "git merge"&lt;/li&gt;
&lt;li&gt;Create standardised Git workflows to safeguard your ongoing development cycle&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Turning the code for an existing project into a local Git repository and creating a commit point
&lt;/h3&gt;

&lt;p&gt;This is quite a complex procedure, but if you take it step by step and use ChatGPT to resolve any problems, you should be fine. Here we go:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;First of all, you'll need to install Git software on your local machine. You'll find instructions for this at &lt;a href="https://git-scm.com/downloads" rel="noopener noreferrer"&gt;Git Downloads&lt;/a&gt;. The library code that this procedure installs on your machine lets you use both "raw" Git commands in terminal sessions and "packaged" Git tools in VSCode. In this post, I'll be using "raw" commands. Horrible as these are, I think they give you a better sense of what's going on than VSCode's built-in tool. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now open a terminal session for your project and type the command:&lt;/p&gt;

&lt;pre&gt;
git init
&lt;/pre&gt;    

&lt;p&gt;This will respond by telling you that an empty git repository has been established in your project. If you look at your project in Windows Explorer, you'll find that you can actually "see" the repository in your project folder - it takes the form of a &lt;code&gt;.git&lt;/code&gt; folder in the project root. This folder contains all the information required to version-control the files in your project, together with a log that stores your commit history. If you ever get into a total mess with your Git install and feel you need to start over, all you need to do is delete this folder! &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;At this point, although you've got a repository, Git isn't actually doing anything useful. None of your files is currently "tracked" by the version control system - that is to say that Git doesn't know which files you want it to manage yet. To request that Git tracks &lt;strong&gt;all&lt;/strong&gt; your files, you can use a &lt;code&gt;git add .&lt;/code&gt; command. But first, note the following two warnings.&lt;/p&gt;

&lt;blockquote&gt;You need to think carefully before you ask Git to track all your files, because you are likely to eventually want to use a server-based remote to back up your local repository. It's important that this doesn't reveal security keys such as your Firebase Project and Service Account Keys. Google patrols GitHub (the website where remote repositories are held), looking for repositories (even private ones) that contain security keys. They will "cane" you if they find you own one. Ensure that any files containing such items are included in your project's &lt;code&gt;.gitignore&lt;/code&gt; file. Also, when working in VSCode, it is recommended that you disable its Smart Commit feature. Use File &amp;gt; Preferences &amp;gt; Settings to search for Smart Commit and disable it if it is enabled.  As mentioned earlier, VSCode provides powerful shortcut tools for managing Git repositories, but these can obscure your understanding of how basic Git commands work. &lt;/blockquote&gt;

&lt;p&gt;Warnings duly noted, submit your &lt;code&gt;add&lt;/code&gt; command:&lt;/p&gt;

&lt;pre&gt;
git add . 
&lt;/pre&gt;

&lt;p&gt;Ignore any warnings about line-ending inconsistencies here.  You can confidently leave VSCode and Git to sort this out between them. Your files are now tracked by Git.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now create a &lt;code&gt;commit&lt;/code&gt; point for your local repository with a second new Git command:&lt;/p&gt;

&lt;pre&gt;
git commit -am "Your commit message"
&lt;/pre&gt;

&lt;p&gt;Here, the "Your commit message" field is a unique tag that identifies the state of your repository at this point and enables you to restore it later, if required. Writing "commit messages" is an art form. You're forced by the system to keep the first line of the message short - 50 characters max. A typical commit statement would look like &lt;code&gt;git commit -am "Stable login added"&lt;/code&gt;. The "-a" flag here tells git to include all tracked files that have been modified or deleted, and the "m" flag tells it to label the commit with the supplied message. A useful command that gives you a simple list of all the commits you've made to your local repo is &lt;code&gt;git log --oneline&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  3. Recovering to a commit point
&lt;/h3&gt;

&lt;p&gt;This is where things start to get useful. Try a little experiment. Make a few random changes to several files in your project and save them. Then, imagine you've changed your mind and want to revert your project to its initial state.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Type the following into your terminal session:&lt;/p&gt;

&lt;pre&gt;
git restore .
&lt;/pre&gt;

&lt;p&gt;Now, check your project. Yes, all the changes made since your last commit have been washed away - as before, the "." option has told Git to apply the command to all files. Wonderful!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The simple "restore" command introduced above restores your project to the most recent commit. How would you proceed if you wanted to restore an earlier commit? First, you need to find the "short hash" - the code that Git itself uses to identify commit points. There are several ways of doing this, but since you're using terminal sessions here, I recommend that you type in the following: &lt;/p&gt;

&lt;pre&gt;
git log --pretty=format:"%h : %s"
&lt;/pre&gt;

&lt;p&gt;The result will be a list of all your commits. Individual entries here will look like: &lt;code&gt;c9f6037 : Stable login added&lt;/code&gt;. The &lt;code&gt;c9f6037&lt;/code&gt; bit here is the "short hash" for the full 40-character commit identifier. You can now restore all files and folders in your project to the "Stable login added" commit point with the following command: &lt;/p&gt;

&lt;pre&gt;
git restore --source c9f6037 .
&lt;/pre&gt;

&lt;p&gt;Other forms of the command are available to restore selected elements. Ask ChatGPT for guidance if you find you need it.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  4. Creating a remote Git repo on the web and synchronising this with your local repo.
&lt;/h3&gt;

&lt;p&gt;As suggested earlier, creating a remote repo on the web will provide a secure backup for your local work. This will involve some investment of your time, but I can guarantee that the time will come when you'll be glad you made it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;To get started, you first need to create a GitHub account. GitHub is a developer platform owned and operated by Microsoft. You can create a (free - thank you, Microsoft) account at &lt;a href="https://github.com" rel="noopener noreferrer"&gt;Github Home Page&lt;/a&gt;, but I'm not going to describe this procedure here because it's a bit complicated and may change in future. Ask ChatGPT for advice if you get into trouble.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Once you're through this and logged into GitHub, click your profile icon, select "repositories/your repositories" (an empty list at present) and click the "New Repository" button. Give your repository a name - you'll probably want to choose something based on your VSCode project name - and tell GitHub whether it is to be public or private. If you select public, others can view it on the web and "clone" it (i.e., download it) as a local repository on their devices. You'll probably want to select "private" at this stage - you can always change your mind later. Decline creating .&lt;code&gt;gitignore&lt;/code&gt; or &lt;code&gt;readme&lt;/code&gt; files too - you'll complicate things enormously if you add anything to your new remote repo at this point. You can also safely ignore the offer to create a license. For now, simply click the green "Create repository" button at the bottom of the page. This will create a remote repository that is now waiting for you to populate it with a copy of your local project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Back in VSCode, you now need to link your local repository to your new remote repository and "push" your local content into it. You do this with a series of Git commands submitted via a terminal session.  To save your typing, GitHub will have provided the following default script for you to cut and paste: &lt;/p&gt;

&lt;pre&gt;
git remote add origin https://github.com/mygitaccount/myreponame
git branch -M main
git push origin main
&lt;/pre&gt;

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

&lt;ul&gt;
&lt;li&gt;The "add origin" command links your local repo to the "remote" at "&lt;a href="https://github.com/mygitaccount/myreponame" rel="noopener noreferrer"&gt;https://github.com/mygitaccount/myreponame&lt;/a&gt;". &lt;/li&gt;
&lt;li&gt;The "branch -M main" bit forcibly renames your local repo as "main". This post hasn't introduced you to the concept of "branches" yet, so just park this for the present. It will make sense shortly.&lt;/li&gt;
&lt;li&gt;The "push" command copies the content of your local repo to a GitHub version with a branch named "main". &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can confirm this by clicking "myreponame" on GitHub's "My Repositories" page and exploring its file hierarchy. You should find that the storage here mirrors your local repo.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;With your remote repo in place as a remote copy of your project source, you can now experiment with the procedure for maintaining it. Repeat your previous exercise: make a few changes to some testbed project files and use VSCode to commit them, as described earlier, with a command such as &lt;code&gt;git commit -am "New Changes added"&lt;/code&gt;. Now push your local repo to its linked remote repository with the following command:&lt;/p&gt;

&lt;pre&gt;
git push origin main
&lt;/pre&gt;

&lt;p&gt;A visit to your repository's GitHub page should confirm that the changes have been received. Note that this is not just a copy of the latest state of your project's files - GitHub now contains the full history of your project. In a team development context, GitHub provides a shared space for developers to coordinate their work.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you want, you can leave things here - you now have a secure backup copy of your project's code and a mechanism for maintaining it. But unless you're developing something purely to satisfy your own interests, there's a lot more to think about. In a high-stakes, commercial environment, the "pipeline" that deploys development work into production storage and thence to users' desktops is a complex, rigorously controlled procedure. Please read on to get some insight into the world of "Continuous Integration" (CI).&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Creating "branches" on your local and remote repositories to separate your production code from development code
&lt;/h3&gt;

&lt;p&gt;Suppose you've begun work on an enhancement to the current version of your system, and a crisis arises in the implemented version: a fault has been found, and a fix is urgently needed. What do you do? If you apply the fix to the code in your local repository, you may deploy untested development code along with the fix. Once you've experienced this situation, you will never want to find yourself there again! Let's stop it from happening.&lt;/p&gt;

&lt;p&gt;The answer is obvious: create separate production and development environments within your local repository. Git "branches" were developed specifically to enable you to do this.&lt;/p&gt;

&lt;p&gt;At the point when you last deployed your application, your local repository had only one branch - the "main" one you created at the outset. The following Git command creates a &lt;strong&gt;new&lt;/strong&gt; branch called "dev" and switches you to it &lt;/p&gt;

&lt;pre&gt;
git switch -c dev
&lt;/pre&gt;

&lt;p&gt;You can always confirm which branch you're on by running &lt;code&gt;git status&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You would create a new branch when you want to initiate a new line of development. Git enables you to do this without interfering with stable code. When the work is ready to be implemented, Git enables you to merge the changes safely into the main project history.&lt;/p&gt;

&lt;p&gt;One important point to note is that when you created as above, this is not a &lt;strong&gt;copy&lt;/strong&gt; of the "main" branch. Rather, it is a "peg" to which new versions of files representing a development track can be pinned&lt;/p&gt;

&lt;p&gt;Also, naively, you might imagine that when working on your new "dev" branch, your view of files on "main" will be unchanged. This is not automatically the case. If you try changing "dev" branch files and switching back to their "main" versions, you'll find that the changes appear here too. How do you make branches work as expected?&lt;/p&gt;

&lt;p&gt;First of all, you need to understand that file changes only get "pinned" to a branch when you "commit" them to it.  You do this by making a commit on the branch:&lt;/p&gt;

&lt;pre&gt;
git commit -m "Change test.txt on dev"
&lt;/pre&gt; 

&lt;p&gt;But if you do this right now, you'll find that you get a &lt;code&gt;Changes not staged for commit&lt;/code&gt; error message followed by a list of the files that currently aren't "staged". So, what is staging?&lt;/p&gt;

&lt;p&gt;Take a deep breath. &lt;/p&gt;

&lt;p&gt;You need to be clear in your mind that, just as files are known to git only when you "track" them, files are included in a commit only when they are "staged". Even if your file was already tracked (because it was in your file hierarchy when you created your repository and ran &lt;code&gt;git add .&lt;/code&gt;), you've now edited it, so a new version has been created. This is currently "dangling" loose. Git will only know which branch to pin it to when you "add" it to the staging area and commit to that branch. &lt;/p&gt;

&lt;p&gt;When staging files, you &lt;strong&gt;could&lt;/strong&gt; do them individually with &lt;code&gt;git add myfile.text&lt;/code&gt; or, alternatively, do a preliminary &lt;code&gt;git add .&lt;/code&gt; to add all current unstaged files. But most people will use the following command:&lt;/p&gt;

&lt;pre&gt;
git commit -am "Change test.txt on dev"
&lt;/pre&gt;

&lt;p&gt;This stages and commits all tracked and modified files in one go. However, if you had created some new files, you would need to get these tracked before committing, so it would be better to cover all bases with&lt;/p&gt;

&lt;pre&gt;
git add .
git commit -am "Change test.txt on dev"
&lt;/pre&gt;

&lt;p&gt;Try this out by switching to dev and changing the content of a test file. Now save the file and do a "git commit -am "Change test.txt on dev". When you switch back to &lt;code&gt;main&lt;/code&gt; with a &lt;code&gt;git switch main&lt;/code&gt; and re-open the test file, you should find that the changes are absent. Well done! You've now got the basic skills you need to create a robust production/development codebase control system. &lt;/p&gt;

&lt;p&gt;Let's put this into practice. Assuming you have now acquired a Git account and successfully pushed your "main" local branch to a remote copy, here's how you would start from scratch to set up a clean dev branch mirroring your current main branch, and then give this its &lt;strong&gt;own&lt;/strong&gt; remote copy.&lt;/p&gt;

&lt;p&gt;First, clear the decks by making absolutely sure that "main" is clean and synced.&lt;/p&gt;

&lt;pre&gt;
git switch main
git fetch origin
git status
&lt;/pre&gt;

&lt;p&gt;You should see: "On branch main"&lt;/p&gt;

&lt;p&gt;Since your current &lt;code&gt;dev&lt;/code&gt; branch is only experimental, you should now reset it cleanly as an exact copy of your local "main" production branch&lt;/p&gt;

&lt;pre&gt;
git branch -D dev # `-D` = force delete local branch only.
git switch -c dev # create fresh dev branch from main
&lt;/pre&gt;

&lt;p&gt;At this point, dev is an exact copy of main. It's time now to complete the picture by giving the dev branch its own backup on the remote server. You do this with the following one-line command :&lt;/p&gt;

&lt;pre&gt;
git push origin dev
&lt;/pre&gt;

&lt;p&gt;You should see output like:&lt;/p&gt;

&lt;pre&gt;
remote: 
remote: Create a pull request for 'dev' on GitHub by visiting:
remote:
To [remote repo address]
 " * [new branch]      dev -&amp;gt; dev
branch 'dev' set up to track 'origin/dev'"
&lt;/pre&gt;

&lt;p&gt;This looks as if it's asking you to do something, but it's actually simply a combined conversation between Git on the server and git on the local repo that says, "Your remote repository did not previously have a dev branch. I have now created it". A visit to your remote repo should confirm this.&lt;/p&gt;

&lt;p&gt;Perhaps you're surprised that you don't have to reference the url of the remote Git server when you launched the &lt;code&gt;git push origin dev&lt;/code&gt; push command. This is because you're just creating a branch here, not a new remote. But it's a sophisticated command that does the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;creates &lt;code&gt;origin/dev&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;uploads history&lt;/li&gt;
&lt;li&gt;links local dev to remote dev (otherwise kknow as "upstream tracking")&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you've done this, you can backup your local dev branch whenevr you like by switching to it and issuing a simple &lt;code&gt;git push&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Merging a branch - how to forward a "hot-fix" on "main" into "dev"
&lt;/h3&gt;

&lt;p&gt;Once you've implemented a change to the production system you'll want to see this cleanly transmitted to the dev branch. You do this using the Git "Merge" command:&lt;/p&gt;

&lt;pre&gt;
git switch dev   # Switch to the branch you want to merge INTO
git merge main   # Merge dev's changes INTO dev
&lt;/pre&gt;

&lt;p&gt;This looks simple enough, but if you think hard about the complex conditions the process may encounter, your head will start to ache!&lt;/p&gt;

&lt;p&gt;The best way of thinking about this is that merge "combines the independent changes both branches have made since they split".&lt;/p&gt;

&lt;p&gt;This will be fine until it is found that the two branches changed the same thing in different ways. For example, one branch might have changed a file to say &lt;code&gt;const x = 3;&lt;/code&gt; while the other changed it to &lt;code&gt;const x = 4&lt;/code&gt;. In this situation, Git will report the problem as a "conflicted file", abandon the merge and leave you to sort things out.&lt;/p&gt;

&lt;p&gt;A typical conflicted file error message will look like this:&lt;/p&gt;

&lt;pre&gt;
CONFLICT (content): Merge conflict in temp.js
Automatic merge failed; fix conflicts and then commit the result.
&lt;/pre&gt;

&lt;p&gt;It's easy to panic in this situation because it often occurs at points of maximum jeopardy in the development cycle.  Also, Git does nothing to soften the blow - to quote an unknown developer, it "punishes you cryptically".&lt;/p&gt;

&lt;p&gt;Here's what you should do in this situation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Don’t panic (nothing is broken)&lt;br&gt;
. Git has paused the merge intentionally.&lt;br&gt;
. Your repo is safe.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Back in your VSCode editor, the lines in the temp.js file that are causing the problem will have been "marked up" with something like:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; HEAD
const x = 4;
=======
const x = 3;
&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; main (Incoming Change)
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is a "graphical" representation of the problem with the two sections of conflicted code laid out on either side of a "fence" represented by the "============" characters. On the "uphill" side of the fence, you have HEAD, the destination version of the code (dev), and on the "downhill" side (incoming change), you have the source (main)&lt;/p&gt;

&lt;p&gt;What you need to decide is "what should this file say?” You need to design the final state, and the answer will usually be one of:&lt;br&gt;
    . keep one side of the fence only (and delete the other)&lt;br&gt;
    . or combine both&lt;br&gt;
    . or rewrite entirely&lt;/p&gt;

&lt;p&gt;In practice, your files will contain a number of these markers and your task now is to edit your conflicted files until all the &amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;, ======= and &amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; markers have gone, leaving only lines that state precisely "what this file should do"&lt;/p&gt;

&lt;p&gt;You're nearly there, but there's one final step you need to take before your &lt;code&gt;git merge&lt;/code&gt; will work properly.&lt;/p&gt;

&lt;p&gt;When a conflict occurs, Git no longer has a normal version of the file. Instead, it temporarily stores three versions of the file in the Git repo's index:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;merge base (the file versions' common ancestor)&lt;/li&gt;
&lt;li&gt;your "uphill" branch (dev/HEAD)&lt;/li&gt;
&lt;li&gt;your "downhill" branch (main) &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you manually fix the file, you create a fourth version — the correct merged result. But Git does not assume you’re finished. You must explicitly confirm that the conflict has been resolved by "re-tracking" the file:&lt;/p&gt;

&lt;pre&gt;
git add temp.js
&lt;/pre&gt;

&lt;p&gt;You do this on the "uphill" "Head" branch (&lt;code&gt;dev&lt;/code&gt; in this case) and follow it with a &lt;code&gt;git commit -am "merge test complete"&lt;/code&gt; or similar.  Git should now respond by telling you that the merge has been completed successfully&lt;/p&gt;

&lt;p&gt;Addendum. The &lt;code&gt;git merge&lt;/code&gt; command joins two branches in a way that preserves the history of commits on both partners. There is another form of join - the &lt;code&gt;git rebase&lt;/code&gt; command that joins the partners in a way that pretends that all the commits on the "uphill" branch happened &lt;strong&gt;after&lt;/strong&gt; the commits on the "downhill" branch. You would use this when you simply want to "tidy" your branch and aren't concerned about maintaining a rigorous commit history. This is for experts only and won't be discussed further here  &lt;/p&gt;
&lt;h3&gt;
  
  
  7. Putting it all together - safeguarding your ongoing development cycle with Git
&lt;/h3&gt;

&lt;p&gt;Life as a solo-developer situation usually consists of making frequent fixes and minor enhancements to the &lt;code&gt;main&lt;/code&gt; production branch while more protracted work on a major extension continues on the &lt;code&gt;dev&lt;/code&gt; branch. Each of these will ultimately lead to re-deployment of the application.&lt;/p&gt;

&lt;p&gt;Here's the barebones of the two deployment procedures:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Minor enhancements to main&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;
git switch main
# Move to the main branch (deployment branch)

git status
# Verify what will be committed; ensure no unexpected changes

git add .
# Stage all your changes (or specific files, if preferred)

git commit -m "Fix: "
# Create a permanent commit representing exactly what you will deploy

git push origin main
# Upload this commit to the remote repository
# Remote now becomes the source of truth for the deployment

# Run deployment script at this point - always runs on main

git switch dev   
# Now prepare to bring your dev branch up to date

git merge main   
# Merge main's changes into dev

git commit       
# Only if conflicts arise (a successful merge commits automatically )

git push origin dev 
# update dev's backup on the remote

&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Implementation of dev branch&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;
git switch dev
git log dev..main --oneline
# check that main contains no commits that are absent from dev

git add .
git commit -m "Super new feature"
# Commit your new feature on dev

git push origin dev
# update the dev branch on the remote

git switch main
git merge dev
# bring main up to date with dev enhancement

git push origin main
# update main's backup on the remote

# Run deployment script at this point - always runs on main
&lt;/pre&gt;

&lt;p&gt;If you ask nicely, ChatGPT will give you scripts to automate these processes.&lt;/p&gt;
&lt;h3&gt;
  
  
  8. Authentication issues and SSH
&lt;/h3&gt;

&lt;p&gt;One of the most annoying issues you may encounter when using Git on Microsoft Windows to push local changes to the remote occurs when your terminal session cheerfully reports "Error: repository not found".&lt;/p&gt;

&lt;p&gt;Of course, the repo &lt;strong&gt;may&lt;/strong&gt; have been deleted, but this is unlikely. The message is usually just a smokescreen stating that VSCode has been unable to authenticate you with Git.&lt;/p&gt;

&lt;p&gt;The most likely reason is that you have been working on multiple projects, each with its own Git repo and associated security keys. In this instance, the error message indicates that VSCode tried to push to your local repo with the wrong set of keys.&lt;/p&gt;

&lt;p&gt;When you first pulled or pushed to your remote repo for a project, VSCode would have asked you to log in to that repo. At this point, it will have saved a copy of these in the Windows Credential Manager. This saves you the trouble of re-entering them the &lt;strong&gt;next&lt;/strong&gt; time you want to access the repo. The problem is that, by default, the Credential Manager only holds a copy of the first entry you use. So, when you switch to another project, you're served incorrect credentials.&lt;/p&gt;

&lt;p&gt;There are several ways to fix this. One is simply to open the credential manager and delete the Git entry you see there. When you try to push to the remote now, you'll be prompted to log in to Git, and an appropriate new entry will be created.&lt;/p&gt;

&lt;p&gt;A better way is to clear out the Git entry, then prevent the problem from recurring by telling Windows to store permissions per repository. You do this by opening a terminal session on each of your projects and running 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;git config &lt;span class="nt"&gt;--local&lt;/span&gt; credential.helper manager-core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, there's a more interesting way of resolving these problems. Here's the background:&lt;/p&gt;

&lt;p&gt;For serious IT developments, the security of the codebase stored in a Git repo is a major concern. A huge amount of capital will be invested in its creation, and it is a juicy target for competitors keen to poach others' work. How safe are keys stored in Credential Manager, and how safe is the traffic passing from VSCode to Git when you sync an update or pull a new remote copy? If these thoughts concern you, you might find it useful to explore an alternative protocol called SSH (Secure Shell).&lt;/p&gt;

&lt;p&gt;SSH was developed in 1995 by Tatu Ylönen. It's a cryptographic network protocol used to securely access and manage systems over the internet. It allows for secure remote login and command execution on another machine, as well as file transfers between systems. It works by establishing an encrypted communication channel that prevents third parties from eavesdropping or tampering with the communication.&lt;/p&gt;

&lt;p&gt;For present purposes, because Git allows you to log in using SSH keys as well as the conventional HTTPS-based arrangement you've used so far, SSH provides an alternative to Windows Credential Manager for repo authentication. Suffice it to say that SSH's many security and practical advantages make it the standard security mechanism for Git operation by professional IT teams. &lt;/p&gt;

&lt;p&gt;When you have a spare moment and would like to explore something new, interesting, and useful, I suggest you ask ChatGPT for an SSH tutorial. The technology has applications in many areas beyond Git, so time spent here will be time well invested. &lt;/p&gt;

</description>
      <category>beginners</category>
      <category>git</category>
      <category>github</category>
      <category>webdev</category>
    </item>
    <item>
      <title>NgSysV2-5.4: Browser Cookies</title>
      <dc:creator>MartinJ</dc:creator>
      <pubDate>Tue, 03 Feb 2026 16:20:51 +0000</pubDate>
      <link>https://dev.to/mjoycemilburn/ngsysv2-54-browser-cookies-532i</link>
      <guid>https://dev.to/mjoycemilburn/ngsysv2-54-browser-cookies-532i</guid>
      <description>&lt;p&gt;&lt;em&gt;This post series is indexed at &lt;a href="https://ngatesystems.com" rel="noopener noreferrer"&gt;NgateSystems.com&lt;/a&gt;. You'll find a super-useful keyword search facility there too.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Last reviewed: February 2026&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Introduction
&lt;/h3&gt;

&lt;p&gt;Most times you open a new webapp, these days, you are greeted by an annoying pop-up that tells you "this site uses cookies" and asks what you want done with them. Here's an example&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%2Fb0aovesly9r6bwnv5m3q.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%2Fb0aovesly9r6bwnv5m3q.png" alt="Dulux website GDPR popup" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is GDPR, the EU's "General Data Protection Regulation" in action. Although it is unlikely that more than one in a hundred of us have the faintest idea of why a cookie might be (or even care, particularly), you won't be permitted to enter the app until you submit to this interrogation. &lt;/p&gt;

&lt;p&gt;This post attempts to clarify the situation by explaining:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what a browser cookie is&lt;/li&gt;
&lt;li&gt;where you can see them&lt;/li&gt;
&lt;li&gt;what their purpose is&lt;/li&gt;
&lt;li&gt;why they may pose a danger to you&lt;/li&gt;
&lt;li&gt;how GDPR and other standard bodies are working to minimise your risk&lt;/li&gt;
&lt;li&gt;how you may assist their work by taking commonsense precautions &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the process, the post will also attempt to untangle the vicious knot of terminology that infests the subject:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;first-party cookies&lt;/li&gt;
&lt;li&gt;third-party cookies&lt;/li&gt;
&lt;li&gt;third-party identity cookies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sounds like fun? This post certainly lays bare some of the web's grubbier secrets&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Cookie Basics - First-party Cookies
&lt;/h3&gt;

&lt;p&gt;A cookie is a packet of information held in the user's browser storage. To view cookies for a website in a browser such as Google Chrome, right-click anywhere on the page, select Inspect to open Developer Tools, click the Application tab, and expand the Cookies section in the left sidebar. You'll now see a table listing the cookies associated with your website's domain. The graphic below shows the cookies that have been created by the "dev.to" website to help me write this post:&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%2Fsea8sijq6djgkzob3mk9.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%2Fsea8sijq6djgkzob3mk9.png" alt="Dev.to cookies" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The most basic form of cookie provides a way of enabling persistent website behaviour between sessions - for example, you might use a cookie to let the user configure the language or the dark/light mode setting to be used on the website's pages. These are called first-party cookies because they are available only to pages on the website that set them.&lt;/p&gt;

&lt;h4&gt;
  
  
  Setting a basic (first-party) cookie:
&lt;/h4&gt;

&lt;p&gt;Here's some JavaScript that you might use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lang=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;language&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suggestion: Why not try this now using the Inspector's console? Set a cookie with the value "eng", for example, check that you can see it in &lt;code&gt;storage/cookies&lt;/code&gt; and then delete it with "right-click/delete".&lt;/p&gt;

&lt;p&gt;This creates a cookie called &lt;code&gt;lang&lt;/code&gt; with the value of the &lt;code&gt;language&lt;/code&gt; variable. In passing, you might like to know that the &lt;code&gt;document.cookie&lt;/code&gt; method also allows you to set attributes of the cookie, such as its lifetime and its scope (i.e., which pages can access it).&lt;/p&gt;

&lt;h4&gt;
  
  
  Retrieving a first-party cookie:
&lt;/h4&gt;

&lt;p&gt;This is a bit harder because you have to find a named cookie in a browser's "cookie-jar" that contains cookies with many other names. The following would do the trick:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;lang&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookie&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lang=&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;split&lt;/span&gt;&lt;span class="p"&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you run this, the &lt;code&gt;lang&lt;/code&gt; cookie's value will be set on the &lt;code&gt;language&lt;/code&gt; variable.&lt;/p&gt;

&lt;p&gt;Here are some more examples of useful first-party cookies:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Session Management &amp;amp; Authentication&lt;/strong&gt;: These are cookies that keep you signed in to websites, allowing you to move from page to page without re-entering your password. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shopping Carts &amp;amp; E-commerce&lt;/strong&gt;: These are cookies that store items in a shopping cart, allowing users to add products and continue shopping or return later to complete a purchase.&lt;/p&gt;

&lt;p&gt;The important point to remember about first-party cookies is that information accumulated here is accessible only to the first-party app. This post now describes how other types of cookie can be used to pass information silently to other websites.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Third-party cookies
&lt;/h3&gt;

&lt;p&gt;Here's a screenshot of the cookie storage from another site. It's the helpful word-finder webapp I use when desperate to complete a crossword.&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%2F5tk1loq4uqspmdnxccsx.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%2F5tk1loq4uqspmdnxccsx.png" alt="crosswordsolver.org/ tracker cookies" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that the familiar first-party cookie entry for the &lt;code&gt;crosswordsolver.org&lt;/code&gt; domain is now followed by a long list of cookie entries for other domains (eg &lt;code&gt;visitor.omnitagis.com&lt;/code&gt;). Hmm - what are these doing there?&lt;/p&gt;

&lt;p&gt;The word finder site is very useful to me, but it has never charged me for the service. How can this be? The truth is that behind the scenes, I'm paying for it in another way. This site has negotiated deals with advertisers who pay the site for advertising space. Fair enough, you might say, but there is more to this deal than meets the eye. If I click on an ad that has caught my eye, I will find that, behind the scenes, I have got more than I bargained for. The advertiser will have set a cookie in my browser storage that contains a unique code. Whenever I use crosswordsolver.org in the future, the advertiser will be informed. It doesn't know &lt;strong&gt;who&lt;/strong&gt; I am, but it does know that the same person is making a return visit.&lt;/p&gt;

&lt;p&gt;Even more sinisterly, if I click on other domains that carry ads for the same agency, the agency site will again be informed of my visit. This is because the cookie is shared. I am now being tracked cross-site. &lt;/p&gt;

&lt;p&gt;Cookies like those owned by &lt;code&gt;visitor.omnitagis.com&lt;/code&gt; in the Inspector's "cookies" view of the crosswordsolver.org site are "third-party" cookies. They appear here because the &lt;code&gt;visitor.omnitagis.com&lt;/code&gt; site is embedded somewhere in the crosswordsolver.org site - probably as an advert. The information they supply is used to deliver personalised, targeted advertisements relevant to user interests. Suppliers are happy to pay considerable sums to agencies if they increase sales. I myself might be pleased to be alerted to products I might be interested in. But not always ....&lt;/p&gt;

&lt;h4&gt;
  
  
  The early history of third-party cookies
&lt;/h4&gt;

&lt;p&gt;You might wonder why anybody ever thought that third-party cookies were a good idea. It seems, however, that browser designers thought third-party websites should be provided with a mechanism to configure persistent user state just like first-party sites. For example, a user of an embedded map should be able to set a region or location preference and see it restored after a session break. For simplicity, the designers opted to preserve this state in cookie storage and then sought ways to create and access third-party cookies.&lt;/p&gt;

&lt;h4&gt;
  
  
  Setting a third-party cookie
&lt;/h4&gt;

&lt;p&gt;Third parties cannot use the &lt;code&gt;document.cookie&lt;/code&gt; method to set a cookie for the very good reason that they are in a different domain. What they &lt;strong&gt;can&lt;/strong&gt; do, however, is add a &lt;code&gt;Set-Cookie&lt;/code&gt;header to the response to a file request from the first-party site. Typically, this request would be received when the browser is loading the embedded third-party site.&lt;/p&gt;

&lt;p&gt;To set a &lt;code&gt;uid&lt;/code&gt; parameter in the third-party cookie, the third-party's server might use code like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Set-Cookie&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;uid=ABC123; Path=/; SameSite=None; Secure&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/path/to/file.html&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;On successful receipt of &lt;code&gt;file.html&lt;/code&gt;, the browser would detect the header and set the cookie as instructed.&lt;/p&gt;

&lt;h4&gt;
  
  
  Reading a third-party cookie
&lt;/h4&gt;

&lt;p&gt;The "headers" trick is used again here. Obviously, the third-party can't directly address the first-party and &lt;strong&gt;ask&lt;/strong&gt; for its cookie. In fact, it doesn't have to because the browser sends the third-party's cookie as a standard &lt;code&gt;Cookie&lt;/code&gt; header with &lt;strong&gt;every&lt;/strong&gt; file request to the third party's files.&lt;/p&gt;

&lt;p&gt;The Get command launched by the browser would typically look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;GET&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="nx"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;1.1&lt;/span&gt;
&lt;span class="nx"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;
&lt;span class="nx"&gt;Cookie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;ABC123&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;prefs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;dark&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  How third-party cookies track user activities
&lt;/h4&gt;

&lt;p&gt;In order to display an ad, a domain like &lt;code&gt;https://www.crosswordsolver.org/&lt;/code&gt; must launch a "Get" instruction to obtain files containing images, scripts, etc., from the advertising agent's website. As described above, if there is a cookie for this third-party website, the browser &lt;strong&gt;automatically&lt;/strong&gt; adds it as a &lt;code&gt;Cookie&lt;/code&gt; header to the &lt;code&gt;Get&lt;/code&gt; request.&lt;/p&gt;

&lt;p&gt;Initially, the browser won't have a cookie for this particular third party. Recognising this, the third-party server will duly create one by adding a &lt;code&gt;set-cookie&lt;/code&gt; header to its response. The content of the cookie thus created will be innocuous - simply a unique identifier looking something like the &lt;code&gt;uid=ABC123&lt;/code&gt; example used above. But this is the point at which the third party has created the opportunity to track the user's subsequent activity - potentially indefinitely.&lt;/p&gt;

&lt;p&gt;On &lt;strong&gt;every&lt;/strong&gt; subsequent visit by the user to the website, the third party will be contacted again. This is because at least one of the files referenced by the ad will have been tagged with "no-cache," which requires a refresh. To minimise impact on site performance, this will be a tiny dummy file displaying a blank 1*1 pixel image or a minimal JS &lt;code&gt;fetch&lt;/code&gt;. As before, the third-party cookie with its unique tag will be set, and the third party will know that "this user" has visited the site again. If the user actually clicks an ad, more information will be gathered, and the agency will begin to build a picture of the user's interests.&lt;/p&gt;

&lt;p&gt;But worse still, as mentioned earlier, if the third-party domain has ads on other sites, the cookie created by your use of the first site &lt;strong&gt;will also take effect here&lt;/strong&gt;. This is because cookies are stored in the browser's cookie storage solely by their third-party domain. The user is now said to be tracked "cross-site". It's important to recognise that, at this stage, the agency doesn't know who you are - it only knows that this is the same person it has seen previously. But as you'll see shortly, over time and through cross-matching with other information sources, identification may be possible.&lt;/p&gt;

&lt;p&gt;This tracking will persist until you clear your browser's cookie history. As you'll know, this is a very disruptive action (deleting useful sign-in credentials, etc., along with any third-party cookies), so you won't be in a hurry to do it too often!&lt;/p&gt;

&lt;h4&gt;
  
  
  Finally, what a third party realistically infer from tracking data.
&lt;/h4&gt;

&lt;p&gt;(a) &lt;strong&gt;You are the same user across many sites&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If cookie &lt;code&gt;ID=9f3a8c2e1b&lt;/code&gt; appears on, say, news-site.com, diy-forum.net, travel-deals.org, the tracker infers: “These visits came from the same browser/device, so they most likely reflect the interests of the same person.”&lt;/p&gt;

&lt;p&gt;(b) &lt;strong&gt;These are your interests&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;From page context and ad clicks, trackers may infer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shopping intent (e.g. “barbecue equipment”, “insurance quotes”)&lt;/li&gt;
&lt;li&gt;Hobbies (DIY, cycling, photography)&lt;/li&gt;
&lt;li&gt;Travel plans (locations, timing)&lt;/li&gt;
&lt;li&gt;Media preferences (politics, sport, finance)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This works even if you never click an ad.&lt;/p&gt;

&lt;p&gt;(c) &lt;strong&gt;This is your approximate location&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;From the originating site's IP address, Language and locale, repeated patterns, trackers might infer&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Country&lt;/li&gt;
&lt;li&gt;City or region&lt;/li&gt;
&lt;li&gt;Sometimes workplace vs home patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(d) &lt;strong&gt;This is the sort of device you are using&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;From the request headers information, the tracker might be able to determine the type of device you are using, and whether this is one person or multiple people sharing the same device&lt;/p&gt;

&lt;p&gt;(e) &lt;strong&gt;These are your behavioural patterns&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;Over time, trackers may infer: Daily routines, Active hours, Workdays vs weekends, Session length and frequency&lt;/p&gt;

&lt;p&gt;This is often more identifying than name or email.&lt;/p&gt;

&lt;p&gt;(f) &lt;strong&gt;This is the  sort of person you are&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Without knowing &lt;em&gt;who&lt;/em&gt; you are, trackers can infer: Age bracket, Income range, Family status, Education level&lt;/p&gt;

&lt;p&gt;(g) &lt;strong&gt;Potentially - this is who you are&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On its own, the cookie is pseudonymous, but there is no guarantee that, given the opportunity to access publicly available information from other sites, a tracker could not make an informed guess about &lt;strong&gt;who&lt;/strong&gt; you are.&lt;/p&gt;

&lt;p&gt;In short, over time, a third party may build a cross-site profile of browsing behaviour and inferred interests, which may be commercially valuable for targeted advertising.&lt;/p&gt;

&lt;p&gt;The sensitivity here is that the information is gathered covertly. The arrangement is now seen as abusive, and standards lobbies have united in their determination to see standards created that will put a stop to these practices&lt;/p&gt;

&lt;h4&gt;
  
  
  What has been done to curb the "threat" posed by tracker cookies?
&lt;/h4&gt;

&lt;p&gt;Two points before answering this question:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Not all tracking is contentious. For example, tracking arrangements such as "Google Analytics" are invaluable in enabling sites to gain information about user behaviour - which pages are visited, how long they stay, and what buttons are clicked. This helps website owners explore performance issues and improve functionality. &lt;/li&gt;
&lt;li&gt; Tracking monetises user activity, thereby funding many free services that one has become accustomed to taking for granted. Would it be a better world if one had to pay for every Google search or ChatGPT query in exchange for assurance that such use remained strictly private?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That said, you might like to know that among major browsers, Safari, Firefox and Brave now block third-party cookies by default. Chrome and Edge still permit them for now, but are actively phasing them out.&lt;/p&gt;

&lt;p&gt;Also, where third-party cookies are still permitted, they must explicitly label themselves as such by setting the &lt;code&gt;SameSite=None; Secure&lt;/code&gt; properties. This makes cross-site cookies more visible and auditable, both by browsers and regulators.&lt;/p&gt;

&lt;p&gt;But even when third-party cookies are blocked, I regret to say that there are many other ways advanced technology can still track our activity online. Even without cookies, a server can correlate requests using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IP address&lt;/li&gt;
&lt;li&gt;timing&lt;/li&gt;
&lt;li&gt;your browser's "signature"&lt;/li&gt;
&lt;li&gt;request patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While these data are individually weak, collectively they are strong. The information they provide is probabilistic, but often good enough.&lt;/p&gt;

&lt;p&gt;See the next section for some advice on common-sense precautions you might employ to limit your exposure&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Third-party identity cookies
&lt;/h3&gt;

&lt;p&gt;In an ordinary third-party cookie, the "payload" will contain only an anonymous (but unique) identifier. But if the cookie was created by a website that signed you in with an Identity Provider such as Google, there was previously an opportunity for the identity cookie to carry your email address.&lt;/p&gt;

&lt;p&gt;Clearly, the information gathered by such a third-party &lt;strong&gt;identity&lt;/strong&gt; cookie is enormously more valuable - and potentially enormously more dangerous to the individual that is being tracked.&lt;/p&gt;

&lt;p&gt;Recent moves by apps to routinely use "federated sign-in" to authorise users would appear to magnify this risk enormously. The good news is that authentication gateways such as "Sign in with Google" operate under a new FedCM standard that is explicitly designed to prevent Google itself from setting a third-party identity cookie on the site.&lt;br&gt;
See &lt;a href="https://dev.to/mjoycemilburn/an-introduction-to-sign-in-with-google-google-one-tap-and-fedcm-2p8m"&gt;"Sign in with Google", "Google One Tap" and "FedCM"&lt;/a&gt; for further information&lt;/p&gt;

&lt;p&gt;The bad news is that the app itself &lt;strong&gt;does&lt;/strong&gt; know the user identity, and there is nothing, in principle, to stop them from selling this information to third-party advertisers on their site. &lt;/p&gt;

&lt;p&gt;In principle, this is where GDPR comes in. GDPR is a legal framework that requires apps to disclose how they use your data. An app would experience a massive backlash if it were ever revealed to be systematically exploiting the trust that its users had placed in it&lt;/p&gt;

&lt;p&gt;But in such a complex and obscure environment, it clearly pays to be cautious. Privacy-conscious users should ring-fence their activities and use different identifiers for different services.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Summary and practical takeaways
&lt;/h3&gt;

&lt;p&gt;Browser cookies began life as a simple, pragmatic solution to a real problem: how to make a stateless web feel usable. Over time, however, the same mechanism was repurposed to support large-scale, cross-site tracking — often without users’ knowledge or meaningful consent.&lt;/p&gt;

&lt;p&gt;Modern browsers and regulators have now recognised that this arrangement has gone too far. Measures such as SameSite restrictions, third-party cookie blocking, cookie partitioning, and FedCM are all attempts to restore a balance between functionality, privacy, and transparency. They do not eliminate tracking entirely, but they do make covert, ambient tracking much harder to justify and much easier to see.&lt;/p&gt;

&lt;p&gt;For users, a few common-sense conclusions follow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Not all cookies are bad — many are essential to how the web works.&lt;/li&gt;
&lt;li&gt;First-party cookies generally pose limited risk when used responsibly.&lt;/li&gt;
&lt;li&gt;Third-party cookies enable cross-site tracking and deserve greater scrutiny.&lt;/li&gt;
&lt;li&gt;Consent prompts exist because the underlying practices were opaque, not because cookies themselves are inherently dangerous.&lt;/li&gt;
&lt;li&gt;Blocking third-party cookies improves privacy, but it does not make tracking impossible.&lt;/li&gt;
&lt;li&gt;Awareness — not paranoia — is the most effective defence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For developers, the lesson is equally clear: privacy-respecting design is no longer optional, and mechanisms that rely on silent cross-site state are living on borrowed time.&lt;/p&gt;

&lt;p&gt;The web is in the middle of a long, imperfect correction. Understanding how we got here is the first step toward using it more thoughtfully.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>privacy</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>"Sign in with Google", "Google One Tap" and "FedCM"</title>
      <dc:creator>MartinJ</dc:creator>
      <pubDate>Fri, 16 Jan 2026 19:15:17 +0000</pubDate>
      <link>https://dev.to/mjoycemilburn/an-introduction-to-sign-in-with-google-google-one-tap-and-fedcm-2p8m</link>
      <guid>https://dev.to/mjoycemilburn/an-introduction-to-sign-in-with-google-google-one-tap-and-fedcm-2p8m</guid>
      <description>&lt;p&gt;Last reviewed: Jan '26&lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;You'll surely have noticed a growing trend toward apps signing users in with a "Sign In with Google" (or similar) identity provider (IdP) procedure. &lt;/p&gt;

&lt;p&gt;This post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Explains specifically how "Sign in with Google" works&lt;/li&gt;
&lt;li&gt;Discusses why &lt;strong&gt;you&lt;/strong&gt; might want to use it&lt;/li&gt;
&lt;li&gt;Provides a Svelte/Firebase example&lt;/li&gt;
&lt;li&gt;Describes the history of FedCM.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  "Sign in with Google"
&lt;/h3&gt;

&lt;p&gt;In the FedCM model, an IdP like Google lets you use a single account to sign in to multiple applications without creating new passwords.&lt;/p&gt;

&lt;p&gt;Once "Sign in with Google" verifies that you are who you say you are (by virtue of your knowledge of an account's password and any associated 2FA credentials), it generates a cryptographically secure token for the app to pass to its secure backend processing. Here, the app extracts your account ID from the token and checks whether it is on its "whitelist" of permitted accounts.&lt;/p&gt;

&lt;p&gt;The interaction is triggered by the display of a standard &lt;strong&gt;Sign in with Google&lt;/strong&gt; 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%2Ftl8lnevioauwvgokg8hs.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%2Ftl8lnevioauwvgokg8hs.png" alt="Sign in with Google image" width="800" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In a conventional sign-in, a webapp would use its own code to request and validate credentials. "Sign in with Google" enables you to replace this with Google library code. You also benefit from Google's secure cookies that allow Google to recognise returning users and issue fresh ID tokens. Your app can then sign in returning users with minimum fuss. &lt;/p&gt;

&lt;p&gt;Here's an example of the dialogue (account id's have been blurred):&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%2Ftl2f2f71zeftn0kwa7i8.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%2Ftl2f2f71zeftn0kwa7i8.png" alt="Google login account selection popup" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, you are explicitly told which app you're logging into and are assisted in selecting the Google account to use (you may have many - not necessarily all Gmail accounts). You are also given the opportunity to check which items (eg your name) may be supplied to the app from the information registered in your Google account.&lt;/p&gt;

&lt;p&gt;Once signed in to a Google account, cookie caching can keep you signed in for a surprisingly long time. During this time, you may leave the browser, turn your computer off and back on again and still find yourself logged into the app. &lt;/p&gt;

&lt;p&gt;But eventually, Google will decide that your "auth" is no longer valid and that it's time to re-authenticate.&lt;/p&gt;

&lt;p&gt;Recognising that this creates significant "friction" for users, app developers can enable Google to let you sign back in with a &lt;strong&gt;One Tap&lt;/strong&gt; prompt rather than the relatively cumbersome "Sign in with Google" procedure. The essence of this arrangement is that Google offers to skip the explicit account selection step of "Sign in with Google" by using the previously used account. The "One Tap" prompt appears as an ephemeral pop-up (formatted and displayed entirely by Google) that is floated alongside the conventional "Sign in with Google" button. &lt;/p&gt;

&lt;p&gt;Here's a screenshot showing the interaction&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%2Fa6fw9j52q6f9nb7x9ddw.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%2Fa6fw9j52q6f9nb7x9ddw.png" alt="Display of ephemeral" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the ephemeral pop-up displays the account details for the targeted account (assuming it's still signed into Google) and invites you to re-enter the app by tapping the "Continue as ..." button. If you are unhappy that Google has selected the correct account, you can simply ignore the ephemeral and "Sign in with Google" instead. &lt;/p&gt;

&lt;p&gt;As you may imagine, a good deal of Google magic is concealed within this procedure. All I'll say about it is that, in my experience, it seems to work well, but is difficult to test. This is because eligibility depends on prior user interaction, dismissal history, and browser heuristics that developers cannot directly control. More on this later.&lt;/p&gt;

&lt;p&gt;Finally, I should say that in practice, you'll likely want to display your "Sign in with Google" button alongside those for other IdPs, such as Apple and Facebook. Here's an example of such an arrangement from the &lt;a href="https://accounts.binance.com/en-GB/login" rel="noopener noreferrer"&gt;Binance&lt;/a&gt; site.&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%2F4145y9zmhyeyij26j1j9.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%2F4145y9zmhyeyij26j1j9.png" alt="Binance login screen" width="800" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that this shows the use of "Continue with Google" as an alternative to "Sign in with Google" in the button text - see the &lt;code&gt;sign-in-with-google&lt;/code&gt; code below for instructions on how to control both this and other characteristics of the button. Note also that the Binance login screen carries a button to register a Binance account for a user id. This is where Binance deploys the code to maintain its customer "whitelist".&lt;/p&gt;

&lt;h3&gt;
  
  
  Should you use "Sign in with Google"?
&lt;/h3&gt;

&lt;p&gt;Here are some pros and cons:&lt;/p&gt;

&lt;p&gt;(a) &lt;strong&gt;Reasons why you might be delighted to use a Federated Sign In&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;(1) It makes life easy for both users and developers&lt;/p&gt;

&lt;p&gt;(2) A federated sign-in, such as "Sign in with Google", follows the FedCM (Federated Credential Management) standard. FedCM, ensure that the federated Identity Provider (Google, for example) cannot silently follow the user across apps. In this case, Google would only know that you signed in to an app, not how you used it or how that usage relates to activity elsewhere. &lt;/p&gt;

&lt;p&gt;(b) &lt;strong&gt;Reasons why you might be reluctant&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;(1). For as long as I can recall, web users have been advised not to share passwords between apps. Now, IdPs like Google appear to be explicitly encouraging developers to write apps that authenticate using a common password.&lt;/p&gt;

&lt;p&gt;For example, if your Google account is ever compromised, all associated "Sign in with Google" app activity is likewise at risk.&lt;/p&gt;

&lt;p&gt;The first counter-argument is that security arrangements for IdP accounts are fearsome. I can guard my Google passwords with a variety of two-factor authentication methods, and Google will alert me to sign-ins from strange devices. It also provides recovery mechanisms to make it easy to reset a compromised password. &lt;/p&gt;

&lt;p&gt;Developing a local sign-in to match such levels of sophistication would be quite a challenge.&lt;/p&gt;

&lt;p&gt;The second counter-argument is that you don't *&lt;em&gt;need *&lt;/em&gt; to pin all your app activity to a single IdP password because IdPs like Google let you create multiple accounts. You can still have a different account for each app if this is what you want. &lt;/p&gt;

&lt;p&gt;Nevertheless, while Federated logins let you create a new account on the spot, it will clearly be a good idea to anticipate user pushback to federated sign-in by offering a wide range of IdP alternatives.&lt;/p&gt;

&lt;p&gt;(2) Google's "One Tap" button means that a user may prolong the life of a sign-in indefinitely&lt;/p&gt;

&lt;p&gt;The first counter-argument to this is that if you instinctively dislike this arrangement, it's not a requirement. The code examples below show you how to suppress "One Tap". &lt;/p&gt;

&lt;p&gt;The second counter-argument is that if you want to withdraw a user's access to an app because, say, they have become "persona non grata", you retain the ultimate sanction of removing their Id from your app's whitelist.&lt;/p&gt;

&lt;p&gt;(c) &lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Overall, the view seems to be that federated sign-in offers both a superior user experience and greater security. While I don't expect to see it used for the most critical applications, I'll certainly be using it in my own work. As a huge Google fan, my eyes light up when I visit a new app and see a "Sign in with Google" button.&lt;/p&gt;

&lt;p&gt;My only caveat is that I would expect a mainstream app that uses FedCM sign-on for public access to also maintain a private, heavily restricted backdoor using conventional technology. Corporate employees operate in different circumstances and require exceptional control. &lt;/p&gt;

&lt;h3&gt;
  
  
  How to code for "Sign in with Google"?
&lt;/h3&gt;

&lt;p&gt;For your project to use "Sign in with Google", it must first register with the Google Cloud console, obtain a Google API client ID, and provide various details about itself. &lt;/p&gt;

&lt;p&gt;(1) Get a Google API client ID for your project&lt;/p&gt;

&lt;p&gt;If you are using Firebase, you will already have registered your project in the Google Cloud console and received a Google Project ID. But now you need an additional Google API &lt;strong&gt;Client ID&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;The procedure is straightforward and well documented by Google at &lt;a href="https://developers.google.com/identity/gsi/web/guides/get-google-api-clientid" rel="noopener noreferrer"&gt;Get your Google API client ID&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Once you've got your Client Id, add it to your project's .env file. The entry should look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;VITE_GOOGLE_CLIENT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;108&lt;/span&gt; &lt;span class="p"&gt;..&lt;/span&gt; &lt;span class="nx"&gt;obfuscated&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="nx"&gt;m2gjhau&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;googleusercontent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(2) Configure an OAuth Consent Screen&lt;/p&gt;

&lt;p&gt;The Google document also describes the procedure for completing an OAuth Consent Screen, which "tells users the application requesting access to their data, what kind of data they are asked for and the terms that apply". Importantly, it also tells Google which 'authorised domains' it may accept, i.e. the web addresses in your app that are permitted access. For testing my webapp, I only needed &lt;a href="http://localhost:5173" rel="noopener noreferrer"&gt;http://localhost:5173&lt;/a&gt; (though obviously, you'd add others for live purposes later)&lt;/p&gt;

&lt;p&gt;(3) Build yourself a "sign-in-with-google" page. &lt;/p&gt;

&lt;p&gt;This is a page that will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;load the "Sign in with Google" library&lt;/li&gt;
&lt;li&gt;configure the Google Identity service&lt;/li&gt;
&lt;li&gt;show the One Tap" ephemeral (if required)&lt;/li&gt;
&lt;li&gt;render a standard "Sign in with Google" button&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the code that I've been using. Since I'm a Firebase developer working on the Svelte platform, this may not map exactly to your case, but I hope you'll still find it useful.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;sign&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kd"&gt;with&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;google&lt;/span&gt;&lt;span class="o"&gt;/+&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;svelte&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;onMount&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;svelte&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;goto&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$app/navigation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signInWithGoogleIdToken&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$lib/firebase.client.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;returnTo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Load the Google Identity Services library&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadGoogleScript&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;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;google&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://accounts.google.com/gsi/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;script&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;// Create a callback function to return control when Google successfully&lt;/span&gt;
  &lt;span class="c1"&gt;// returns an ID token&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleCredentialResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;signInWithGoogleIdToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;goto&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="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;returnTo&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Firebase sign-in failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;onMount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Parse the redirectTo parameter from the current URL&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;urlParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;returnTo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;urlParams&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;returnTo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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;// Load the GIS library i&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loadGoogleScript&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Configures Google Identity Services (GIS). Nothing visible happens yet -&lt;/span&gt;
    &lt;span class="c1"&gt;// - you have just set the rules.&lt;/span&gt;
    &lt;span class="nx"&gt;google&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accounts&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="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VITE_GOOGLE_CLIENT_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;handleCredentialResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;auto_select&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;cancel_on_tap_outside&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="c1"&gt;// you have now:&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. Identified your app&lt;/span&gt;
    &lt;span class="c1"&gt;// 2. Set a callback (handleCredentialResponse(credential)) that will fire&lt;/span&gt;
    &lt;span class="c1"&gt;//    when Google successfully authenticates the user.&lt;/span&gt;
    &lt;span class="c1"&gt;// 3. Told Google not to sign the user in if Google already knows who they are.&lt;/span&gt;
    &lt;span class="c1"&gt;//    (Important for avoiding “why did it log me in?” surprises.)&lt;/span&gt;
    &lt;span class="c1"&gt;// 4. Told Google that if the user clicks elsewhere, it should dismiss One Tap cleanly.&lt;/span&gt;

    &lt;span class="c1"&gt;// At this point:&lt;/span&gt;
    &lt;span class="c1"&gt;//    - No sign-in has happened&lt;/span&gt;
    &lt;span class="c1"&gt;//    - No UI has appeared&lt;/span&gt;
    &lt;span class="c1"&gt;//    - You’ve just set the rules&lt;/span&gt;

    &lt;span class="c1"&gt;// Tell Google to show the One Tap Sign-on if appropriate. Omit the following&lt;/span&gt;
    &lt;span class="c1"&gt;// statement if you don't want this&lt;/span&gt;
    &lt;span class="nx"&gt;google&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accounts&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="nf"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Specify the format of the "Sign in with Google" button&lt;/span&gt;
    &lt;span class="nx"&gt;google&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accounts&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="nf"&gt;renderButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;googleBtn&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;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;outline&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;large&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;signin_with&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// or "continue_with"&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Skip page if already signed in&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onAuthStateChanged&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;returnTo&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;// This is completely independent of Google Identity Services. What happens here:&lt;/span&gt;
  &lt;span class="c1"&gt;//  1. Firebase checks its persisted session (IndexedDB)&lt;/span&gt;
  &lt;span class="c1"&gt;//  2. When resolved, it calls the callback once&lt;/span&gt;
  &lt;span class="c1"&gt;//  3. If user exists: the user is already authenticated you immediately redirect to /returnTo&lt;/span&gt;
  &lt;span class="c1"&gt;//  4. If user is null: stay on this page and let "One Tap" or "Sign In button do their work&lt;/span&gt;
  &lt;span class="c1"&gt;//&lt;/span&gt;
  &lt;span class="c1"&gt;// Key insight - you are coordinating three independent state machines:&lt;/span&gt;
  &lt;span class="c1"&gt;//  - Google Identity UI&lt;/span&gt;
  &lt;span class="c1"&gt;//  - Firebase authentication&lt;/span&gt;
  &lt;span class="c1"&gt;//  - SvelteKit navigation&lt;/span&gt;
  &lt;span class="c1"&gt;//&lt;/span&gt;
  &lt;span class="c1"&gt;// In summary: This code sets up Google sign-in UI, lets the user authenticate&lt;/span&gt;
  &lt;span class="c1"&gt;// if needed, and uses Firebase as the single source of truth to redirect authenticated&lt;/span&gt;
  &lt;span class="c1"&gt;// users without races, loops, or double sign-ins.&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;min-h-screen flex items-center justify-center bg-slate-50&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bg-teal-200 border border-black rounded-xl
           px-[10vh] py-[10vh]
           max-w-md w-full
           flex justify-center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rounded-md overflow-hidden&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;googleBtn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TLDR: Notes&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The code above declares a function to load the Google Identity Services library. This is used to ensure the load completes successfully when the function is deployed later in the code. Google Docs say: "Be sure to load [the library] on any page that a user might sign in on". Naively, I thought this could be achieved by simply adding it to my project's app.html file. This didn't work at all! Svelte's complex hydration means you need to explicitly load it on your pages. The asynchronous code to load the library is called here with an &lt;code&gt;await&lt;/code&gt;. It is defined as a function in case circumstances change, and I need to centralise it as a "helper"&lt;/li&gt;
&lt;li&gt;Confusingly, while Google Docs talk about the "Google Identity Services library", the library is actually held in "&lt;a href="https://accounts.google.com/gsi/client" rel="noopener noreferrer"&gt;https://accounts.google.com/gsi/client&lt;/a&gt;"; GSI ("Google Sign In") is the historical API surface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(4) Create a Svelte &lt;code&gt;fedcm-test&lt;/code&gt; route that triggers a login and displays some test data.&lt;/p&gt;

&lt;p&gt;The following &lt;code&gt;+page.svelte&lt;/code&gt; testbed triggers a sign-in to get a Google ID and, if successful, passes this to a &lt;code&gt;+server.svelte&lt;/code&gt; file that checks if the user's account contained in the Google ID is included in the app's "whitelist" of permitted users. If the user is accepted, this server then returns a list of &lt;code&gt;dataEntry&lt;/code&gt; items from a Firestore &lt;code&gt;test_fedcm_data&lt;/code&gt; collection and &lt;code&gt;+page.svelte&lt;/code&gt; displays them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;fedcm&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="o"&gt;/+&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;svelte&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;onMount&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;svelte&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$lib/firebase.client.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;dataEntries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;onMount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Not authenticated&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;// pass the user's idToken (containing their email address&lt;/span&gt;
      &lt;span class="c1"&gt;// and other pieces of user information) to the secure &lt;/span&gt;
      &lt;span class="c1"&gt;// server-based code in fedcm-test/+page.server &lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;idToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getIdToken&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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/fedcm-test&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;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;headers&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;Content-Type&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;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;idToken&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;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nx"&gt;dataEntries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataEntries&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;dataEntries&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="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;min-h-screen flex items-center justify-center bg-slate-50&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex flex-col items-center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!--&lt;/span&gt; &lt;span class="nx"&gt;Teal&lt;/span&gt; &lt;span class="nx"&gt;panel&lt;/span&gt; &lt;span class="o"&gt;--&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;
      &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;
        bg-teal-200 border border-black text-black
        rounded-xl
        w-[75vw] h-[75vh]
        md:w-[50vw] md:h-[50vh]
        flex
      &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;
        &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;
          w-full h-full
          overflow-y-auto overflow-x-hidden
          overscroll-contain
          pl-[15%] pr-4 py-4
        &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;{:&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-red-600&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;{:&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ul&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;space-y-2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt; &lt;span class="nx"&gt;dataEntries&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;dataEntry&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&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;dataEntry&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/li&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sr"&gt;/each&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ul&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sr"&gt;/if&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(5) Create a server to check that the user is permitted to use the webapp and to return data for display&lt;/p&gt;

&lt;p&gt;Here, in Svelte, I use a &lt;code&gt;+server.js&lt;/code&gt; file. This means that all the processing here occurs securely on a cloud host (as opposed to insecurely using JavaScript on a web client). Because I'm using Google's Firebase, this requires me to obtain a Google service account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;fedcm&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="o"&gt;/+&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@sveltejs/kit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;admin&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase-admin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;serviceAccount&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/secrets/service-account-file.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Retrieves the Google Account Id for the logged in user and check whether&lt;/span&gt;
&lt;span class="c1"&gt;// this appears in the "test_fedcm_whitelist" table. If so, it returns the &lt;/span&gt;
&lt;span class="c1"&gt;// contents of the test_fedcm_data table. &lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// a firebase service account is required in order to authorise server-side &lt;/span&gt;
  &lt;span class="c1"&gt;// firestore access. This is drawn from the project's "secrets" file&lt;/span&gt;
  &lt;span class="nx"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initializeApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;serviceAccount&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;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;firestore&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&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;authHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;authorization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;authHeader&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bearer &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Missing auth token&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;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;401&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;idToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;authHeader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bearer &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;verifyIdToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;idToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// idToken has been encrypted by "Sign in with Google" using Google's &lt;/span&gt;
    &lt;span class="c1"&gt;// private keys.  verifyIdToken() now validates it using Google's &lt;/span&gt;
    &lt;span class="c1"&gt;// public keys and checks that it:&lt;/span&gt;
    &lt;span class="c1"&gt;//    . Was issued for your project (aud claim)&lt;/span&gt;
    &lt;span class="c1"&gt;//    . Comes from a trusted issuer (iss claim)&lt;/span&gt;
    &lt;span class="c1"&gt;//    . Hasn’t expired (exp claim)&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userEmail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;decoded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;userEmail&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="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No email on auth token&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;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Check that the user's Google Account Id is included in the list &lt;/span&gt;
    &lt;span class="c1"&gt;// of authorised users&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fedcmWhitelistSnap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test_fedcm_whitelist&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;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;whitelistEmail&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="nx"&gt;userEmail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fedcmWhitelistSnap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;empty&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="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Access not allowed&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;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Fetch the list of fedcmData entries and return it as a JSON&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fedcmDataSnap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test_fedcm_data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dataEntry&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;get&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;dataEntries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fedcmDataSnap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;dataEntry&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;dataEntries&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Server error&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;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&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;(6) Create some data in Firestore:&lt;/p&gt;

&lt;p&gt;The code above assumes that you have also created two Firestore collections:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;test_fedcm_whitelist&lt;/code&gt; containing a set of &lt;code&gt;whitelistEmail&lt;/code&gt; id values for accounts you want to have access to the app&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;test_fedcm_data&lt;/code&gt; containing a set of &lt;code&gt;dataEntry&lt;/code&gt; test items you want  the app to display &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(6) Create a listener to ensure that all pages for this app trigger a login request before proceeding&lt;/p&gt;

&lt;p&gt;In Svelte, I do this by creating a &lt;code&gt;+layout.svelte&lt;/code&gt; file. By placing this at the top of the &lt;code&gt;fedcm-test&lt;/code&gt; folder tree, I ensure that login will be enforced in all subordinate routes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;fedcm&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="o"&gt;/+&lt;/span&gt;&lt;span class="nx"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;svelte&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;onMount&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;svelte&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$lib/firebase.client.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;goto&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$app/navigation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$app/stores&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;onMount&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;// set a listener that will be called whenever a page loads. If &lt;/span&gt;
    &lt;span class="c1"&gt;// the user hasn't got a Google Auth (possibly because this has &lt;/span&gt;
    &lt;span class="c1"&gt;// expired), they are directed to sign-in-with-google&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onAuthStateChanged&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&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;returnTo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nx"&gt;$page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;$page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/sign-in-with-google?returnTo=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;returnTo&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;span class="p"&gt;});&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;slot&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(7) Test the code&lt;/p&gt;

&lt;p&gt;When your app is running in a Svelte project's "dev" server, the browser should respond to a &lt;code&gt;localhost:5173/fedcm-test&lt;/code&gt; URL by displaying a "Sign in with Google" button. Signing in here with a valid Google count that uses a Google account id that is included in the &lt;code&gt;test_fedcm_whitelist&lt;/code&gt; collection should list the items included in the &lt;code&gt;test_fcm_data&lt;/code&gt; collection.&lt;/p&gt;

&lt;p&gt;If you rerun the &lt;code&gt;localhost:5173/fedcm-test&lt;/code&gt;, you should find that "Sign in with Google" is skipped and the &lt;code&gt;test_fedcm_data&lt;/code&gt; items are displayed directly - you are still signed into your Google account, and Google remembers its association with your app. &lt;/p&gt;

&lt;p&gt;Google will break its association with the signed-in account if you wait long enough or use a "Sign out" button in your webapp. I haven't covered "Sign out" here, as this post is already way too long, but ChatGPT will surely give you some pointers. In such cases, you'll see the "Sign in with Google" button when you try to rerun the webapp. &lt;/p&gt;

&lt;p&gt;For testing purposes, another way to force the "Sign in with Google" button to reappear is to run the app in an "incognito" window.&lt;/p&gt;

&lt;p&gt;Display of the ephemeral will typically occur only after a considerable period, when Google decides it is necessary to confirm the webapp's association with a Google Account. It's not easy to trigger this for testing purposes, but I managed to generate the example shown earlier by creating a new Google profile and re-running the app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Background on FedCM
&lt;/h3&gt;

&lt;p&gt;Federated sign-on (or federated identity) emerged from enterprise and academic identity systems in the late 1990s/early 2000s. Instead of each system managing its own usernames and passwords, the idea was to share identities across systems that otherwise retained their own internal autonomy. FedCM is a browser-level evolution of these early ideas. It is Google (and others) saying&lt;/p&gt;

&lt;p&gt;“Federated login is legitimate — let’s make it first-class and privacy-preserving.”&lt;/p&gt;

&lt;p&gt;Development of the W3C FedCM standard has been ongoing since 2021 and has been controversial because it now aims to prevent IdPs such as Google and Facebook from silently tracking users across sites. Nevertheless, in 2024, Google Identity Services migrated “Sign in with Google” to FedCM by default in Chrome.&lt;/p&gt;

&lt;p&gt;The key change introduced by the FedCM standard is that an IdP, such as Google, can no longer set a third-party identity cookie on an app when signing in a user. While the Google GIS library generates the "Sign in with Google" button, this is merely "decorating the doorbell". With FedCM in place, the browser owns the door, and Google's involvement occurs outside the app's "trust domain". &lt;/p&gt;

&lt;p&gt;Sadly, under FedCM, marketing companies that pay to embed adverts in apps are still permitted to create third-party cookies. But because these don't contain full identifying information, they don't pose the acute risk of a third-party &lt;strong&gt;identity&lt;/strong&gt; cookie. At the end of the day, one should perhaps remember that many useful websites rely on income from marketing to stay in business.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>svelte</category>
      <category>google</category>
      <category>auth0challenge</category>
    </item>
    <item>
      <title>NgSysV2-5.3: An Introduction to the Google Cloud Shell</title>
      <dc:creator>MartinJ</dc:creator>
      <pubDate>Mon, 06 Jan 2025 15:30:46 +0000</pubDate>
      <link>https://dev.to/mjoycemilburn/ngsysv2-53-a-young-persons-introduction-to-the-google-cloud-shell-17ki</link>
      <guid>https://dev.to/mjoycemilburn/ngsysv2-53-a-young-persons-introduction-to-the-google-cloud-shell-17ki</guid>
      <description>&lt;p&gt;&lt;em&gt;This post series is indexed at &lt;a href="https://ngatesystems.com" rel="noopener noreferrer"&gt;NgateSystems.com&lt;/a&gt;. You'll find a super-useful keyword search facility there too.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Last reviewed: Jan '25&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Introduction
&lt;/h3&gt;

&lt;p&gt;Would you like a free computer? How about one loaded with useful tools and lots of memory, one that is geared specifically to doing the sort of jobs you've been working on in this series? Interested? If so, please read on.&lt;/p&gt;

&lt;p&gt;This series has recommended the use of Microsoft's VSCode IDE (Interactive Development Environment) as a general-purpose development "hub". As you know, this is the "Swiss army knife" of software that lets you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create and edit source code&lt;/li&gt;
&lt;li&gt;run terminal session to install libraries, start local servers and make deployments&lt;/li&gt;
&lt;li&gt;use Git to commit changes to both local and remote repositories&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But, while you've probably found this all perfectly satisfactory, let me remind you that nothing stands still for long in IT. A fresh wind of change is now blowing through the world of Interactive Development Environments.&lt;/p&gt;

&lt;p&gt;Because your code is designed to deliver applications running out in the "Cloud," people have started to wonder why development source code isn't in the Cloud as well.&lt;/p&gt;

&lt;p&gt;All three major cloud platforms—Google, Amazon, and Microsoft—now offer "Shell" products, which consist essentially of VSCode clones that run in your browser. The development code base is now stored remotely, and build/deployment workflows are closely integrated with their target server platforms.&lt;/p&gt;

&lt;p&gt;Before you panic about learning a whole new set of editing procedures, let me assure you that these new IDEs look startlingly familiar — they just run in the browser rather than as independent local apps. You'll also be interested to hear that the services are free of charge.  &lt;/p&gt;

&lt;p&gt;This post describes, specifically, the Google Cloud Shell (GC Shell). Here's a list of its advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No VSCode installation&lt;/li&gt;
&lt;li&gt;No npm, Node.js or Git installation either - these tools are built into the GC Shell (along with many others)&lt;/li&gt;
&lt;li&gt;Svelte projects can be deployed to services like Firebase Hosting or Google App Engine directly from the shell&lt;/li&gt;
&lt;li&gt;You can work on your code without any preliminary preparation using any device that has a browser and network connection &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the GC Shell lives up to these claims, I imagine you'll easily see how it would have saved you a ton of time and trouble when you were working on your first webapp project. I'm also wondering why I ever bothered telling you anything different. Let's build a Svelte project with the GC Shell and see if the reality is as good as the hype:&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Building a Svelte Project with the Google Cloud Shell (GC Shell)
&lt;/h3&gt;

&lt;p&gt;You start the GC Shell in the browser by clicking its URL at &lt;a href="https://shell.cloud.google.com/?show=ide&amp;amp;environment_deployment=ide" rel="noopener noreferrer"&gt;ide.cloud.google.com&lt;/a&gt;. Actually, let me qualify that - in practice, it seems best to use a right-click and select "open in a new window". This then lets you use the F11 function key to switch the new window into full-screen mode. As you'll know by now, every bit of screen space is precious in an IDE. You won't want to waste any of this on a browser menu.&lt;/p&gt;

&lt;p&gt;What happens now is rather magical. Assuming that you're logged into a Gmail account, Google now sets about creating a virtual computer for you somewhere up in the Cloud. This has up to 5GB of file storage for your code and configuration files (Firestore storage and CPU for a deployed webapp would be billed separately, as normal). Once activated, this "computer" remains permanently associated with your account. Sure, if you leave it unattended for 20 minutes or so, it will quietly close itself down, But once re-reference, your working environment is magically recovered like a desert rose after a shower of rain.&lt;/p&gt;

&lt;p&gt;Naturally, the initial reference takes a little time - 20 seconds or so - and while this is going on, Google takes the opportunity to give you some background information about the GC Shell and then displays a Welcome screen headed "Learn the Essentials". &lt;/p&gt;

&lt;p&gt;This offers opportunities for some initial configuration, too, but I think that you're likely to find that this just adds to your understandable confusion as a first-time user. My suggestion is that you leave everything alone here for now. There'll be plenty of opportunities to explore later.&lt;/p&gt;

&lt;p&gt;Once things settle down, this is what you should see:&lt;br&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%2Fii90azdsi8084x38jigj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fii90azdsi8084x38jigj.jpg" alt="Initial GC Shell display" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope you'll agree that this looks surprisingly VSCode-like. Down the left-hand side of the screen are all the familiar Explorer, Search, Source-control, Debug and Extensions tools. The Edit window itself is still open in the strange Welcome utility, but you can easily imagine this will become the panel in which you edit files. Below this, a terminal session has opened on your Gmail id.&lt;/p&gt;

&lt;p&gt;Things will become even more familiar if you accept the Welcome panel's offer to "Open a Home Directory".  This triggers another wave of GC Shell initialisation, after which (once you've clicked on the Explorer tool), the screen should look like this:&lt;/p&gt;

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

&lt;p&gt;You've now got the beginnings of a working directory in the familiar column adjacent to the tools menu. A README.txt file is open in the editor panel, and you might like to edit this and confirm you can update and save it. The familiar main IDE menu bar is displayed at the top of the screen, too, so take the opportunity to use this to open a terminal panel. Once you've sized things to suit your usual working arrangement, I hope you'll agree that this looks very much like the familiar old VSCode arrangement.&lt;/p&gt;

&lt;p&gt;The next job is to see whether you can use this to install, test and deploy a Svelte webapp. If you have read posts &lt;a href="https://dev.to/mjoycemilburn/ngsysv2-21-installing-sveltekit-and-creating-a-simple-all-html-webapp-3e5h"&gt;2.1&lt;/a&gt; and &lt;a href="https://dev.to/mjoycemilburn/ngsysv2-36-a-serious-svelte-infosys-deploying-to-the-google-cloud-geo"&gt;3.6&lt;/a&gt; in this series you'll remember that this was quite a procedure. Your new GC Shell system makes the job almost trivial!&lt;/p&gt;

&lt;p&gt;The first task is to create a folder for the webapp. Open a terminal on the project root and enter the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir my-svelte-app
cd my-svelte-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now install Svelte into this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm create vite@latest .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm using a Vite install here because it provides a generic tool for selecting Svelte from a long list of alternative Frameworks. You're also invited to add standard libraries, such as Tailwind. This way, you're less likely to forget essential steps in the procedure.&lt;/p&gt;

&lt;p&gt;This Vite installation interface is very similar to the Svelte arrangement. The up/down and space keys are used to select items presented as lists, and the return key activates the selection. If you get stuck, be comforted by the knowledge that all you have to do is delete the &lt;code&gt;my-svelte-app&lt;/code&gt; folder and start again.&lt;/p&gt;

&lt;p&gt;For this particular experiment, you should select Svelte as the Framework and SvelteKit as the Variant. Then choose the SvelteKit minimal template and decline the offer to use Typescript (though it won't be long now before you will know enough about Javascript to appreciate why you will find this useful). I also suggest that you skip the opportunity to add development libraries to this experiment (though in other situations, I myself would certainly have chosen to add "eslint" and "tailwind"). Finally, choose "npm" as your package manager.&lt;/p&gt;

&lt;p&gt;After one final "return", Vite completes the installation and invites you to launch the development server. Give it a try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should display the familiar terminal output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; my-svelte-app@0.0.1 dev
&amp;gt; vite dev


  VITE v5.4.11  ready in 787 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, then, a "ctrl-click" on the &lt;code&gt;http://localhost:5173/&lt;/code&gt; bit of the display should produce the reassuring "Welcome to SvelteKit" output of the barebones Svelte webapp.&lt;/p&gt;

&lt;p&gt;So, that was a good start. Svelte is installed in your new Cloud Computer's workspace, and you can run a development server there. But now, can you use this to build and deploy a public version of the barebones webapp onto the web? &lt;/p&gt;

&lt;p&gt;This seems quite a big ask, and, in fact, while there's nothing to stop you from launching an &lt;code&gt;npm run build&lt;/code&gt; command right now, you'll find this concludes with the following warning message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; Using @sveltejs/adapter-auto
  Could not detect a supported production environment. See https://svelte.dev/docs/kit/adapters 
to learn how to configure your app to run on the platform of your choosing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is telling you that you haven't configured an appropriate Svelte adapter for your project. For Google App Engine targets, you need to install the &lt;code&gt;@sveltejs/adapter-node&lt;/code&gt; adapter as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@sveltejs/adapter-node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you also need to declare your use of "adapter-node" in your project's&lt;code&gt;svelte.config.js&lt;/code&gt; file as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import adapter from '@sveltejs/adapter-node';

export default {
  kit: {
    adapter: adapter(),
  },
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A build should now conclude successfully&lt;/p&gt;

&lt;p&gt;Just three further points need to be covered before you can deploy your webapp.&lt;/p&gt;

&lt;p&gt;First, you need to create an &lt;code&gt;app.yaml&lt;/code&gt; file to direct the build. Here it is:&lt;br&gt;
&lt;/p&gt;

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

env_variables:
  NODE_ENV: production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;nodejs20&lt;/code&gt; confirms that your webapp will run on Node.js version 20, the version currently recommended for App Engine use. Setting &lt;code&gt;NODE_ENV&lt;/code&gt; to &lt;code&gt;production&lt;/code&gt; optimizes the Node.js frameworks and libraries for performance and security.&lt;/p&gt;

&lt;p&gt;Finally, you need to add the following line to the "scripts" section of &lt;code&gt;package.json&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    "start": "node build"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells App Engine to start the Node.js server on the content of your &lt;code&gt;build&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;You're now in a position to deploy your Svelte webapp with a command of the form &lt;code&gt;gcloud app deploy --project=[project-id]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here, &lt;code&gt;[project-id]&lt;/code&gt; is the uniquely qualified ID of a Firebase project that you own. Ah, it's obvious really - how can you deploy to a project in the Google Cloud without telling &lt;code&gt;gcloud&lt;/code&gt; which one it should use? But where will you get this from? Well if you've been following this post series from the beginning, &lt;a href="https://dev.to/mjoycemilburn/ngsysv2-23-creating-a-simple-svelte-information-system-with-googles-firestore-551b"&gt;Post 2.3&lt;/a&gt; will have created a project name based on the root name "svelte-dev" - "svelte-dev-afbaf", say. Why not reuse this here? There's absolutely no reason why not. So, now deploy with the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gcloud app deploy --project=svelte-dev-afbaf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If all goes well, you should now be able to launch your webapp at &lt;code&gt;https://.nw.r.appspot.com&lt;/code&gt; and see, once again, the familiar "Welcome to Sveltekit" message from the barebones Svelte webapp.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Wind-down
&lt;/h3&gt;

&lt;p&gt;It might be worth sitting back for a moment and reflecting on what you've just done.&lt;/p&gt;

&lt;p&gt;With little more than a click on a URL reference, you've just given yourself a top-class editor and a playground to run it in. Then you've used this to build a mini website and launched this onto the web where gentlemen in China could peruse it if they chose. None of this has cost you a penny and, if all went well, probably took you no longer than half an hour. I'd say that was a good morning's work by any standards.&lt;/p&gt;

&lt;p&gt;More seriously, while this has been a very superficial introduction to Cloud Shells, I hope it has given you a taste of what is available. Clearly, there are some disadvantages to a browser-based IDE. Startup is noticeably slow and I shall be looking for ways of improving this. On the other hand, the close integration with the Cloud platform itself is likely to pay dividends. Some users are likely to be particularly interested in the possibility of using &lt;code&gt;adapter-node&lt;/code&gt; to deploy a webapps to Google Cloud Run, as this has certain advantages over the older App Engine hosting. &lt;/p&gt;

&lt;p&gt;For further details on the GC Shell, check out Google's documentation at &lt;a href="https://cloud.google.com/shell/docs/launching-cloud-shell" rel="noopener noreferrer"&gt;Launch Cloud Shell&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Footnote
&lt;/h3&gt;

&lt;p&gt;If you've been following this post series closely, you may wonder why the GC Shell has enabled you to deploy with the official Svelte &lt;code&gt;adapter-node&lt;/code&gt; generic node.js library rather than the community-supplied open source &lt;code&gt;svelte-adapter-appengine&lt;/code&gt; used in &lt;a href="https://dev.to/mjoycemilburn/ngsysv2-36-a-serious-svelte-infosys-deploying-to-the-google-cloud-geo"&gt;Post 3.6&lt;/a&gt;. This is because GC Shell is tightly integrated with Google Cloud, abstracting many deployment complexities. Locally, you must manage those complexities yourself, and community adapters step in to bridge the gap. While there's absolutely nothing wrong with using the community adapter, some developers might be more comfortable using the official Svelte version.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>googlecloud</category>
      <category>webdev</category>
      <category>devops</category>
    </item>
    <item>
      <title>Using Grammarly in Dev.to</title>
      <dc:creator>MartinJ</dc:creator>
      <pubDate>Mon, 30 Dec 2024 10:43:05 +0000</pubDate>
      <link>https://dev.to/mjoycemilburn/using-grammarly-in-devto-4cd5</link>
      <guid>https://dev.to/mjoycemilburn/using-grammarly-in-devto-4cd5</guid>
      <description>&lt;h3&gt;
  
  
  1. Key Takeaway
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;If you're doing a lot of writing, Grammarly in Free mode will save you a lot of time. Using Grammarly in Pro mode will save you even more time and make your work more professional. It's worth every penny.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When you use Grammarly in Dev.to, one or two tricks will make your life a lot easier. Once you've got the hang of these, you won't want to write your posts any other way.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2. Grammarly - background
&lt;/h3&gt;

&lt;p&gt;I've been using Grammarly for so long that I've almost forgotten I used to do all my spell-checking with a dictionary. Grammarly is now embedded in my Chrome browser as an "extension," and its design is so subtle that I barely notice that I'm using it. &lt;/p&gt;

&lt;p&gt;Until last November, I used Grammarly in "Free" mode, which provided spelling checks and synonyms. But cunningly,  Grammarly began highlighting chunks of my prose that it thought could stand some improvement. In my own case, this was most of it.&lt;/p&gt;

&lt;p&gt;At first, I used this arrangement simply as a cue to shape up and try harder. Sometimes, this worked, and the highlighting would go away. But frustratingly, I often simply couldn't see what Grammarly was getting at. &lt;/p&gt;

&lt;p&gt;However, sometimes, they would give me a "free ride" and show me their suggested improvements. This was almost always far better. So, in November, I took advantage of a "Black Friday" deal and snagged a 12-month license for just £60. They've got me for life now!&lt;/p&gt;

&lt;p&gt;Working with Grammarly in "Pro" mode is a strangely warm, collaborative experience. Richard Brautigan's prophetic 1967 poem "All Watched Over By Machines Of Loving Grace" is never truer than when Grammarly has once again gracefully condensed four lines of my usual waffle into just two lines of lucid English.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Using Grammarly in Dev.to
&lt;/h3&gt;

&lt;p&gt;In the past, I might have used a spell-checker extension in VSCode to build my markdown files. This method had the advantage of giving me a VSCode timeline facility that enabled me to back out changes when I messed up. &lt;/p&gt;

&lt;p&gt;When I decided I wanted to switch to Grammarly, however, I had to leave VSCode behind and build my post using only the post-editing facility in Dev.to. This was fine, except that when I was doing some heavy editing, I found that continually toggling between Edit and Preview modes would make me dizzy. This was because when you enter Preview mode, the browser view resets you to the top of the file. &lt;/p&gt;

&lt;p&gt;If you've been working at the bottom of a large post, scrolling down to recover the edit point makes your eyes boggle. &lt;/p&gt;

&lt;p&gt;Fixing this would be a hard problem for the Dev.to the team, but I've developed my own workaround. It goes like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;When working on a section that is going to require some heavy editing, I highlight a unique snatch of plain text at the top of the section  (it's important to avoid markdown codes) and then ctrl-F this into the browser's text search pop-up.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now I perform my edit, close the search popup and click preview. The page view will now reset to the top of the post&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, to recover my position and view my edit, I reinstate the search popup with another Ctrl-F and click the down arrow. The edit point is now back in view. Four clicks, and I'm done. After a while, it becomes automatic.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  4. A Dev.to post "timeline"
&lt;/h3&gt;

&lt;p&gt;It does worry me that I have no backup for my Dev.to posts. I'm pretty confident that &lt;code&gt;dev.to&lt;/code&gt; won't lose the current version of a post but, from time to time, I find that it would be very useful to recover an earlier version. However, there's no post "timeline" in Dev.to.&lt;/p&gt;

&lt;p&gt;A recent project saw me creating an "index" for a whole series of posts. You can see this at &lt;a href="https://ngatesystems.com" rel="noopener noreferrer"&gt;ngatesystems.com&lt;/a&gt;. A "curated" post series like this is useful in itself because it gives you two bites at getting your posts indexed by Google - once in Dev.to and once in your webapp. But, in this case, there's another benefit. &lt;/p&gt;

&lt;p&gt;This is a &lt;strong&gt;pre-rendered&lt;/strong&gt; Svelte project that creates an in-memory copy of the markdown for the posts referenced in the index. I use it in the webapp to provide a keyword search index for the post series.&lt;/p&gt;

&lt;p&gt;The webapp itself is hosted on the Google Cloud App Engine and is rebuilt nightly using a Windows schedule. This is significant because Google retains deployment history so that, in an emergency, I can revert a faulty build. &lt;/p&gt;

&lt;p&gt;Each of these deployments has, embedded within it, a snapshot of the markdown for the post state at that point. If I need a historic post, I can just temporarily reset the webapp to an appropriate point in the deployment history. Since the webapp is designed specifically to view the posts embedded within it .... Well, it's better than nothing ...&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>git</category>
      <category>github</category>
      <category>webdev</category>
    </item>
    <item>
      <title>NgSysV2-10.1: Firestore CRUD templates</title>
      <dc:creator>MartinJ</dc:creator>
      <pubDate>Tue, 26 Nov 2024 10:08:55 +0000</pubDate>
      <link>https://dev.to/mjoycemilburn/ngsysv2-101-firestore-crud-templates-g83</link>
      <guid>https://dev.to/mjoycemilburn/ngsysv2-101-firestore-crud-templates-g83</guid>
      <description>&lt;p&gt;&lt;em&gt;This post series is indexed at &lt;a href="https://ngatesystems.com" rel="noopener noreferrer"&gt;NgateSystems.com&lt;/a&gt;. You'll find a super-useful keyword search facility there too.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Last reviewed: Nov '24&lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;Google's extensive online documentation for Firestore CRUD (create, read, update, delete) instructions may be too detailed for everyday use. Here are templates for the most important Firestore functions. I suggest you cut and paste them as is and then replace the word "my" in variable names with some suitable contraction of the collection name that you're targetting. For example, references to a collection called "Lecture_events" might be coded as "lecEvtsCollRef".&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating documents
&lt;/h3&gt;

&lt;p&gt;To create a document containing a myDocData object with an &lt;strong&gt;automatically-generated&lt;/strong&gt; id:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;myDocData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="p"&gt;....&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt; &lt;span class="nx"&gt;an&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt; &lt;span class="nx"&gt;containing&lt;/span&gt; &lt;span class="nx"&gt;your&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="nx"&gt;properties&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;myCollRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;myCollectionName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myDocRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myCollRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myDocRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myDocData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that, confusingly,  &lt;a href="https://firebase.google.com/docs/firestore/manage-data/add-data" rel="noopener noreferrer"&gt;Google Documentation&lt;/a&gt; on 'Adding Data' references an &lt;code&gt;addDoc&lt;/code&gt; function as an alternative to &lt;code&gt;setDoc&lt;/code&gt;. See the Postscript below for advice on why &lt;code&gt;setDoc&lt;/code&gt;is preferred. &lt;/p&gt;

&lt;p&gt;In the code snippet above, the &lt;code&gt;myDocRef=&lt;/code&gt; statement is the point at which an auto-id is allocated. If you need to find the value that's been assigned, you'll find this at &lt;code&gt;myDocRef.id&lt;/code&gt;. Again, see the Postscript below for further information on this point.&lt;/p&gt;

&lt;p&gt;To create a document with a &lt;strong&gt;data item&lt;/strong&gt; as its identifier :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;myDocData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="p"&gt;....&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="nx"&gt;object&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;myDocumentIdentifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;....&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt; &lt;span class="nx"&gt;your&lt;/span&gt; &lt;span class="nx"&gt;identifier&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;myDocRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;myCollectionName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myDocumentIdentifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myDocRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myDocData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Reading documents
&lt;/h3&gt;

&lt;p&gt;To retrieve an &lt;strong&gt;individual&lt;/strong&gt; document using its document id:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myDocRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;myCollectionName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myDocId&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;myDoc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myDocRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myDoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Document data:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myDoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&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 retrieve a &lt;strong&gt;selection&lt;/strong&gt; of documents with selection and ordering criteria (example):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myCollRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;myCollectionName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myCollRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;myField1Name&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="nx"&gt;myField1Value&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;myField2Name&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;asc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mySnapshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getDocs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myQuery&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;mySnapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;myDoc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myDoc&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; =&amp;gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myDoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&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;Within a Snapshot's &lt;code&gt;forEach&lt;/code&gt;, the data for a document is available as &lt;code&gt;myDoc.data()&lt;/code&gt;, the document's docRef is &lt;code&gt;myDoc.ref&lt;/code&gt; and its docId as &lt;code&gt;myDoc.id&lt;/code&gt;. If you're just interested in determining the &lt;strong&gt;existence&lt;/strong&gt; of document(s) that match the selection criteria, a useful trick is to check for non-zero &lt;code&gt;mySnapshot.size&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you want to refer to individual documents in the &lt;code&gt;snapshot&lt;/code&gt; array, you'll find the data for the n'th entry at  &lt;code&gt;mySnapshot.docs[n].data()&lt;/code&gt; and its id at &lt;code&gt;mySnapshot.docs[n].id&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Note that if you don't specify an &lt;code&gt;orderBy&lt;/code&gt; field, documents will be returned in ascending order of docId. And if you include more than one &lt;code&gt;where&lt;/code&gt; field, you must create a (compound) index. The browser inspection tool will help you here. You only need to follow the link in the "index-needed" error message. Individual fields are indexed automatically in a Firestore database. &lt;/p&gt;

&lt;p&gt;To retrieve &lt;strong&gt;all&lt;/strong&gt; of the documents in a collection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myCollRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;myCollectionName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myCollRef&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;mySnapshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getDocs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myQuery&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;mySnapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;myDoc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myDoc&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt;= &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myDoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&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;Firestore comparison operators are "==", "&amp;gt;" , "&amp;lt;", "&amp;lt;=", "&amp;gt;=" and "!=", plus some interesting &lt;a href="https://firebase.google.com/docs/firestore/query-data/queries" rel="noopener noreferrer"&gt;array membership&lt;/a&gt; operators.&lt;/p&gt;

&lt;p&gt;To retrieve &lt;strong&gt;all&lt;/strong&gt; of the documents in a &lt;strong&gt;hierarchy&lt;/strong&gt; of collections and &lt;strong&gt;then&lt;/strong&gt; do something:&lt;/p&gt;

&lt;p&gt;You have to be careful when you want to perform a certain action &lt;strong&gt;after&lt;/strong&gt; processing on a multi-level hierarchy of collections has concluded. If your code contains many nested &lt;code&gt;foreach&lt;/code&gt; statements, each containing an &lt;code&gt;await&lt;/code&gt; instruction, you can't rely on the individual &lt;code&gt;awaits&lt;/code&gt; to tell you when the whole set has finished. Each of these individual &lt;code&gt;awaits&lt;/code&gt; occupies a separate thread and these do not communicate directly with each other in any helpful way.&lt;/p&gt;

&lt;p&gt;One way out of this problem is to use the traditional &lt;code&gt;for&lt;/code&gt; loop on your &lt;code&gt;snapshots&lt;/code&gt; rather than &lt;code&gt;forEachs&lt;/code&gt;. Here's an example targeting all the children in a sub-collection before performing an action&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myParentsCollRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;myParentCollectionName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myParentsQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myParentsCollRef&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;myParentsSnapshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getDocs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myParentsQuery&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;myParentsSnapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;myParentDocId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;myParentsSnapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docs&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="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myChildrenCollRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;myParentCollectionName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myParentDocId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;myChildrenCollectionName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myChildrenQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myChildrenCollRef&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;myChidrenSnapshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getDocs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myChildrenQuery&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;myParentsSnapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myChidrenSnapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, you can rely on your &lt;code&gt;await&lt;/code&gt;s to be performed in strict sequence, and when you hit the end of the loop you know you can carry on confidently to perform your dependant action. But the performance hit created by this may be significant and so you might be interested in the following arrangement:&lt;/p&gt;

&lt;p&gt;You can get a handle on the individual &lt;code&gt;promises&lt;/code&gt; launched by the &lt;code&gt;awaits&lt;/code&gt; in a &lt;code&gt;forEach&lt;/code&gt; loop by storing them in an array. You can then apply an &lt;code&gt;await Promise.all&lt;/code&gt; instruction to this array to find out when all its member promises are done. It is impossible to provide a simple template here to suit all circumstances, but the following is a "sketch" that illustrates the broad principles. &lt;/p&gt;

&lt;p&gt;Here, a two-level hierarchy involving two separate collections (parents and children) is linked by a common &lt;code&gt;parentsId&lt;/code&gt; field. The two collections are read into memory to permit analysis of the aggregate. This can only be done when all the children have been read.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;aggregateArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parentsCollRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;parents&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parentsQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parentsCollRef&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;parentsSnapshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getDocs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parentsQuery&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;promisesArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="nx"&gt;parentsSnapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;parentsDoc&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;// for clarity, the nested awaits required to get the children for each parent are coded as an explicit function&lt;/span&gt;
&lt;span class="nx"&gt;promisesArray&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="nf"&gt;fetchChildren&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;parentsDoc&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// and here's the function itself&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchChildren&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parentsDoc&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;childrenCollRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;children&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;childrenQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;childrenCollRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;parentsId&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="nx"&gt;parentsDoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;parentsId&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;childrenSnapshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getDocs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;childrenQuery&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;chidrenSnapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;childrenDoc&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;//push parent and children data into the aggregate array&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// and now you can perform your aggregate analysis. &lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promisesArray&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Updating a document
&lt;/h3&gt;

&lt;p&gt;Example - to change the value of the myField property in a document's myDocData content&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myDocRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myCollectionName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myDocId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myDocRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;myField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;myFieldValue&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example - to replace the entire content of document myDocId with a new object containing only a myField property&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myDocRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myCollectionName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myDocId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myDocRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;myField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;myFieldValue&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;merge&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can apply changes to several fields simultaneously by replacing the &lt;code&gt;{myField: myFieldValue}&lt;/code&gt; bit in the above examples with an object containing the fields you want to change. &lt;/p&gt;

&lt;h3&gt;
  
  
  Deleting a document
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myDocRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myCollectionName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myDocId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;deleteDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myDocRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CRUD operations within transactions
&lt;/h3&gt;

&lt;p&gt;Inside a transaction, the patterns introduced above remain unchanged but the setDoc commands are amended as follows:&lt;/p&gt;

&lt;p&gt;Within the &lt;code&gt;runTransaction(db, async (transaction) =&amp;gt; { ...   }).catch();&lt;/code&gt; function:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;getDoc&lt;/code&gt; is replaced by &lt;code&gt;transaction.get()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;setDoc&lt;/code&gt; is replaced by &lt;code&gt;transaction.set()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deleteDoc&lt;/code&gt; is replaced by &lt;code&gt;transaction.delete()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;span id="1"&gt;&lt;h3&gt;Postscript&lt;/h3&gt;&lt;/span&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;As mentioned above, Google provides &lt;code&gt;addDoc()&lt;/code&gt; and &lt;code&gt;updateDoc()&lt;/code&gt; functions for document creation and update in parallel with &lt;code&gt;setDoc()&lt;/code&gt;. But this seems unnecessarily confusing when setDoc can perform both operations. Also, when it comes to transactions, &lt;code&gt;addDoc()&lt;/code&gt; can only be used to create documents with auto ids. It seems simpler, in practice, just to use setDoc everywhere.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You may have noticed that there's no &lt;code&gt;await&lt;/code&gt; on the &lt;code&gt;doc(myCollRef)&lt;/code&gt; call that creates a Firestore document identifier. This tells you that Firestore somehow manages to do this without actually visiting the collection and seeing what is already in use. If you're curious about how it manages this you might like to check out the discussion at &lt;a href="https://stackoverflow.com/questions/46618719/firestore-are-ids-unique-in-the-collection-or-globally" rel="noopener noreferrer"&gt;StackOverflow&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Google documentation references
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Add data to Cloud Firestore : &lt;a href="https://firebase.google.com/docs/firestore/manage-data/add-data" rel="noopener noreferrer"&gt;https://firebase.google.com/docs/firestore/manage-data/add-data&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Read data with Cloud Firestore : &lt;a href="https://firebase.google.com/docs/firestore/query-data/get-data" rel="noopener noreferrer"&gt;https://firebase.google.com/docs/firestore/query-data/get-data&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Delete data from Cloud Firestore  : &lt;a href="https://firebase.google.com/docs/firestore/manage-data/delete-data" rel="noopener noreferrer"&gt;https://firebase.google.com/docs/firestore/manage-data/delete-data&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SDK documentation can be found at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://firebase.google.com/docs/reference/js/firestore_" rel="noopener noreferrer"&gt;https://firebase.google.com/docs/reference/js/firestore_&lt;/a&gt; and&lt;/li&gt;
&lt;li&gt;&lt;a href="https://firebase.google.com/docs/reference/js/firestore_.transaction" rel="noopener noreferrer"&gt;https://firebase.google.com/docs/reference/js/firestore_.transaction&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>firebase</category>
      <category>firestore</category>
    </item>
    <item>
      <title>NgSysV2-1.1: A Young Person’s Guide to Software Development in the Age of AI</title>
      <dc:creator>MartinJ</dc:creator>
      <pubDate>Tue, 26 Nov 2024 08:13:31 +0000</pubDate>
      <link>https://dev.to/mjoycemilburn/ngsysv2-11-a-young-persons-guide-to-systems-development-in-the-age-of-ai-39pe</link>
      <guid>https://dev.to/mjoycemilburn/ngsysv2-11-a-young-persons-guide-to-systems-development-in-the-age-of-ai-39pe</guid>
      <description>&lt;p&gt;&lt;em&gt;This post series is indexed at &lt;a href="https://ngatesystems.com" rel="noopener noreferrer"&gt;NgateSystems.com&lt;/a&gt;. You'll find a super-useful keyword search facility there too.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Last reviewed: Mar'26&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Introduction
&lt;/h3&gt;

&lt;p&gt;Hello and welcome! I hope you find what you're looking for here.&lt;/p&gt;

&lt;p&gt;This post is for complete beginners. It covers everything I wish I'd known when I first started tinkering with websites and databases. Maybe you're still in school, considering a career change, or retired but curious. What matters is that you have an open mind—one that's ready for new ideas and quick to spot opportunities. You'll need this because modern web development is a jungle of tools and techniques, and it's evolving fast. Where should you even start?&lt;/p&gt;

&lt;p&gt;This series charts a path through that jungle—one that delivers quick results while building the skills you need to eventually go your &lt;strong&gt;own&lt;/strong&gt; way.&lt;/p&gt;

&lt;p&gt;The best part? Almost everything I describe here is &lt;strong&gt;free&lt;/strong&gt;. Your only investment is time, and yes, that won't be insignificant. But as they say, "no pain, no gain!"&lt;/p&gt;

&lt;p&gt;Here's the game-changer: you now have a powerful free ally in AI assistants like ChatGPT. This series includes some long posts with detailed instructions, but they'd be even longer if I explained every step in full. Now, if you get stuck or confused and need advice, just ask ChatGPT (or whatever alternative you favour). It'll give you clear, jargon-free answers instantly. This wasn't possible before 2022. Now it is, and it changes everything. See &lt;a href="https://dev.to/mjoycemilburn/ngsysv2-41-the-age-of-ai-meet-your-digital-tutor-4ocl"&gt;Post 4.1&lt;/a&gt; for more on using your &lt;strong&gt;digital tutor&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;But a word of caution. If you have your sights set on making a career in IT systems development, the landscape here is changing rapidly. Since Christmas 2025, almost all of my own JavaScript has been generated by ChatGPT. My contribution has been conversational English, telling the bot what I want it to do. My productivity has improved tenfold (likewise the quality of my systems, I'm ashamed to admit), but industry-wide, this translates into fewer opportunities for employment. The uncomfortable fact is that, while working in IT has become hugely more enjoyable, there are fewer vacancies for people whose only skill is in programming. The irony here, however, is that only those with a thorough understanding of system development technologies can sensibly direct a bot in system development tasks. How are these people to gain these skills except by learning the hard way and starting their careers as "grunt" programmers? This is the &lt;strong&gt;only&lt;/strong&gt; way you will ever develop the knowledge and instincts required to "orchestrate" the development of an information system. If you still want to build a career as a systems developer, a course like this is exactly the right place to start. The financial rewards will be enormous, but be in no doubt that the competition will be ferocious.&lt;/p&gt;

&lt;p&gt;So, warning delivered, let's get to it. Here's what this series aims to teach you and how we'll get there:&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Objectives
&lt;/h3&gt;

&lt;p&gt;The assumption here is simple: you want to develop software that reads information into a computer, processes it in a purposeful way, and displays the results on a screen. Oh, and on top of that, you’d like anyone, anywhere in the world, to be able to use your work. &lt;/p&gt;

&lt;p&gt;Sounds straightforward? Unfortunately, in the world of IT, there are many ways to achieve this simple task — and choosing the right one matters!&lt;/p&gt;

&lt;p&gt;In my experience, one of the simplest and most flexible approaches is to use an internet browser such as &lt;strong&gt;Chrome&lt;/strong&gt; or &lt;strong&gt;Safari&lt;/strong&gt; as the launchpad for your software. But, you might wonder: isn’t everyone using dedicated apps on their phones these days?&lt;/p&gt;

&lt;p&gt;One key advantage of browsers is that they let your application run across &lt;strong&gt;every conceivable device&lt;/strong&gt; — laptops, tablets, desktops, and phones — without worrying about hardware differences or operating systems. Browsers provide a sophisticated and practical environment for running your application logic. In contrast, native apps are tied to particular platforms and come with added complexity.&lt;/p&gt;

&lt;p&gt;Another advantage is that browsers are already positioned on the Internet, whose whole purpose is to facilitate information sharing. This ubiquity is one reason major platforms such as &lt;strong&gt;Amazon&lt;/strong&gt; and &lt;strong&gt;Facebook&lt;/strong&gt; rely on browser-based interfaces to deliver their services worldwide. &lt;/p&gt;

&lt;p&gt;The route I suggest leads to what is now generally known as a "web application" or &lt;strong&gt;webapp&lt;/strong&gt; — a piece of software activated simply by entering the webapp’s address into a web browser. A modern webapp can maintain persistent data stores (“databases”) and provide security for that data via user login. ChatGPT itself is distributed as a webapp.&lt;/p&gt;

&lt;p&gt;Before we dive into hands-on coding, here’s a brief look at the tools and technologies you’ll need to master to build a modern web application:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;HTML (HyperText Markup Language)&lt;/strong&gt; — the language used to structure and format content in &lt;strong&gt;web pages and web applications&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;IDE (Integrated Development Environment)&lt;/strong&gt; — the editor used to write and manage code; in this course weu’ll be using &lt;strong&gt;Visual Studio Code (VS Code)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;programming language&lt;/strong&gt; — the language in which you implement your application’s logic; we’ll be using the &lt;strong&gt;JavaScript programming language&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browser developer tools&lt;/strong&gt; — tools built into modern browsers for inspecting and debugging &lt;strong&gt;web applications&lt;/strong&gt;; we’ll be using &lt;strong&gt;Chrome Developer Tools&lt;/strong&gt; here.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;web development framework&lt;/strong&gt; — a structured toolset that helps you build web applications efficiently; the framework we’ll use is &lt;strong&gt;SvelteKit&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;backend server platform&lt;/strong&gt; — responsible for hosting the webapp, storing data, and running server-side code; we’ll be using &lt;strong&gt;Google Firebase on App Engine&lt;/strong&gt;, which offers a particularly generous free tier.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In later posts, we’ll look in more detail at topics such as user authentication, databases, deployment, and security — all essential components of a real-world web application.&lt;/p&gt;

&lt;p&gt;Since you’re reading this post online, I’ll assume you’re already reasonably accustomed to the online world. For simplicity, I’ve assumed you’ll be using a Microsoft Windows desktop or laptop, but you should be able to adapt these instructions if you’re on another platform.&lt;/p&gt;

&lt;p&gt;OK. Buckle up - here we go!&lt;/p&gt;

&lt;h4&gt;
  
  
  2.1 HTML (HyperText Markup Language)
&lt;/h4&gt;

&lt;p&gt;You’re developing software that displays text in a web browser. To control how that text appears — its position, size, colour, and emphasis — browsers rely on a system called &lt;strong&gt;markup&lt;/strong&gt;. Markup works by surrounding text with special instructions, called &lt;em&gt;tags&lt;/em&gt;, that tell the browser how to display the content.&lt;/p&gt;

&lt;p&gt;You can try this out immediately using a simple text editor such as Microsoft’s Notepad.&lt;/p&gt;

&lt;p&gt;Type the following line into your editor and save the file with an &lt;code&gt;.html&lt;/code&gt; extension (for example, &lt;code&gt;my-first-app.html&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello there&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now double-click the file. Your default web browser should open and display the words &lt;strong&gt;Hello there&lt;/strong&gt; as a large heading. Congratulations — you’ve written your first webapp (albeit one that only you can see for now).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If this doesn’t work as expected, don’t get stuck. File associations and browser settings vary by device, so this is a good moment to ask ChatGPT for help. Describe what you’re seeing, and it can guide you through fixing the issue.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this example, &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;/h1&amp;gt;&lt;/code&gt; are HTML &lt;em&gt;tags&lt;/em&gt;. They tell the browser that the enclosed text should be displayed as a top-level heading. HTML includes many such tags, each with a specific purpose.&lt;/p&gt;

&lt;p&gt;A good place to start learning HTML is Mozilla’s excellent guide:&lt;br&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web" rel="noopener noreferrer"&gt;Getting Started with the Web&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another popular resource is the interactive&lt;br&gt;
&lt;a href="https://www.w3schools.com/html/" rel="noopener noreferrer"&gt;W3Schools HTML Tutorial&lt;/a&gt;, which lets you experiment directly with HTML syntax.&lt;/p&gt;

&lt;p&gt;You’ll also discover that tags can be &lt;em&gt;qualified&lt;/em&gt; with additional instructions. For example, an &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; tag can include a &lt;code&gt;style&lt;/code&gt; attribute to change its colour:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"color: red;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Hello there&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These styling rules belong to a system called &lt;strong&gt;CSS (Cascading Style Sheets)&lt;/strong&gt;, which we’ll see in action shortly.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.2 The IDE (Integrated Development Environment)
&lt;/h4&gt;

&lt;p&gt;As you’ve probably guessed, even simple web applications quickly grow into &lt;strong&gt;large collections of files&lt;/strong&gt;. Webapp code is verbose, and files containing hundreds or thousands of lines are entirely normal.&lt;/p&gt;

&lt;p&gt;You &lt;em&gt;could&lt;/em&gt; type all of this into a basic text editor like Notepad, but that approach doesn’t scale. Small mistakes — missing brackets, misspelt keywords — are easy to make and can be hard to spot. What you really want is a &lt;strong&gt;specialised editor&lt;/strong&gt; that understands code and helps you write it correctly.&lt;/p&gt;

&lt;p&gt;That’s the role of an &lt;strong&gt;IDE (Integrated Development Environment)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;An IDE monitors your code as you type, highlights its structure, flags obvious errors, and often suggests or completes code for you. Used well, it dramatically reduces frustration and speeds up development.&lt;/p&gt;

&lt;p&gt;There are many IDEs available, but the one I recommend is &lt;strong&gt;Visual Studio Code (VS Code)&lt;/strong&gt;. It’s free, widely used, and well supported by excellent documentation.&lt;/p&gt;

&lt;p&gt;VS Code includes tools to format code consistently, detect syntax errors, and provide auto-completion for things like JavaScript keywords and variable names. As your project grows beyond a single &lt;code&gt;.html&lt;/code&gt; file, it also helps you manage the collection of files that make up your application — what developers refer to as a &lt;em&gt;project&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;For example, VS Code lets you search and edit text across all files at once, undo changes by returning to earlier versions of a file, and run commands directly via an integrated terminal. These features become indispensable as your projects grow.&lt;/p&gt;

&lt;p&gt;This is a good point to install VS Code and work through Microsoft’s short introduction:&lt;br&gt;
&lt;a href="https://code.visualstudio.com/docs/introvideos/basics" rel="noopener noreferrer"&gt;Getting Started with Visual Studio Code&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  2.3 JavaScript
&lt;/h4&gt;

&lt;p&gt;JavaScript is a good first programming language (and might possibly be the only one you ever need). It runs natively in the web browser, where it can read and modify the HTML that defines your web page — put simply, it can change what appears on the screen. It can also communicate with remote servers, making it useful well beyond simple visual effects.&lt;/p&gt;

&lt;p&gt;JavaScript runs directly in the browser, with no separate compilation step, so you can edit code and see the results immediately. Your web browser itself acts as the JavaScript runtime (i.e., the interpreter that converts your code into the low-level instructions that actually display information on your screen).&lt;/p&gt;

&lt;p&gt;Because HTML follows strict structural rules, the browser can build an internal representation of the page called the &lt;em&gt;Document Object Model&lt;/em&gt; (DOM). JavaScript can access and modify individual elements within this model.&lt;/p&gt;

&lt;p&gt;Let’s extend the &lt;code&gt;my-first-app.html&lt;/code&gt; file you created earlier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Hello there&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;hourOfDay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;getHours&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// 0–23&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hourOfDay&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;red&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This version makes the output depend on the time of day. Before midday, the text appears in blue; after midday, it appears in red. You’ve now written a simple &lt;em&gt;dynamic&lt;/em&gt; web application. Congratulations - you're a programmer!&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags tell the browser that the enclosed content is JavaScript code. The expression &lt;code&gt;new Date().getHours()&lt;/code&gt; returns the current hour (from 0 to 23). The &lt;code&gt;if&lt;/code&gt; / &lt;code&gt;else&lt;/code&gt; logic then uses this to decide which colour to apply.&lt;/p&gt;

&lt;p&gt;If you’d rather not wait until lunchtime to test this behaviour, try switching the colour based on whether the current minute is even or odd. You can obtain the minute using &lt;code&gt;getMinutes()&lt;/code&gt; and test for even numbers with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;id="test"&lt;/code&gt; attribute on the &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; tag gives that element a unique identifier. The call to &lt;code&gt;document.getElementById('test')&lt;/code&gt; allows JavaScript to find that element and modify its style.&lt;/p&gt;

&lt;p&gt;If you have any problems getting these heavily abbreviated instructions to work, just cut and paste the code from your file into ChatGPT and tell it what problem you're seeing. It will respond with an explanation of the necessary changes and a corrected version for you to paste back into your file.&lt;/p&gt;

&lt;p&gt;You won’t spend your life writing &lt;code&gt;document.getElementById(...)&lt;/code&gt; calls. Modern frameworks allow you to control screen behaviour using higher-level, more expressive patterns. In this series, you’ll use a framework called &lt;strong&gt;SvelteKit&lt;/strong&gt;, which greatly simplifies this kind of work.&lt;/p&gt;

&lt;p&gt;To become fluent in JavaScript, you’ll need a solid reference. The book I recommend is &lt;strong&gt;&lt;em&gt;Eloquent JavaScript&lt;/em&gt;&lt;/strong&gt; by Marijn Haverbeke. A physical copy is ideal, but the online version is also available. For browser-based learning, Mozilla’s tutorial on &lt;a href="https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/JavaScript_basics" rel="noopener noreferrer"&gt;JavaScript basics&lt;/a&gt; is an excellent alternative.&lt;/p&gt;

&lt;p&gt;Finally, start thinking about a small personal project to practise on. What data would it use? How would users interact with it? These questions will become increasingly important as you progress.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.4 Browser Tools**
&lt;/h4&gt;

&lt;p&gt;Your code won't work perfectly the first time. That's normal. Maybe the layout's off, or your logic has a bug. Sometimes the browser throws an error, sometimes it just sulks silently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your secret weapon: Browser DevTools&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every major browser has built-in debugging tools. In Chrome, just right-click anywhere on your page and select "Inspect." Boom—you're in.&lt;/p&gt;

&lt;p&gt;Yes, it looks intimidating at first — a maze of tabs and panels. But trust me: once you learn the basics, you'll wonder how you ever coded without it. &lt;a href="https://developer.chrome.com/docs/devtools/overview/" rel="noopener noreferrer"&gt;Full Chrome DevTools docs here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you can do with it:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Layout problems?&lt;/em&gt; The inspector shows you exactly how margin, padding, and width are affecting each element. You can even tweak values in real time and see instant results.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Logic problems?&lt;/em&gt; Set breakpoints in your JavaScript. Your code will pause when it hits them, letting you check variable values and step through line by line.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Code crashes?&lt;/em&gt; The console tells you exactly what went wrong and where.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it now:&lt;/strong&gt; Open that &lt;code&gt;my-first-app.html&lt;/code&gt; file from section 2.3, right-click, hit Inspect, and poke around. Change the colour in the Elements tab. Check the Console tab for any messages. Get comfortable — you'll be living here.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.5 SvelteKit
&lt;/h4&gt;

&lt;p&gt;In section 2.3, JavaScript interacted with the page using calls such as &lt;code&gt;document.getElementById(...)&lt;/code&gt;. While this works, it quickly becomes fragile and hard to maintain as applications grow.&lt;/p&gt;

&lt;p&gt;Frameworks such as &lt;strong&gt;SvelteKit&lt;/strong&gt; provide a higher-level interface to the browser’s DOM. Instead of manually locating elements and changing their properties, you describe &lt;em&gt;what should happen&lt;/em&gt;, and the framework keeps the screen in sync with your data. This approach is known as &lt;em&gt;reactivity&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;For example, in Svelte, you can express logic such as “when &lt;code&gt;popupVisible&lt;/code&gt; becomes &lt;code&gt;true&lt;/code&gt;, show the popup”. When the value changes, the framework automatically updates the screen—no explicit DOM manipulation required.&lt;/p&gt;

&lt;p&gt;During development, SvelteKit runs your application using a local development server started from the IDE’s terminal. As you edit your code, the browser refreshes automatically, giving near-instant feedback. This tight edit–refresh loop makes experimentation and learning much easier.&lt;/p&gt;

&lt;p&gt;SvelteKit also allows you to control &lt;em&gt;where&lt;/em&gt; code runs. Some logic executes in the user’s browser, while other logic can run securely on the server. You don’t need to worry about this distinction yet, but it becomes important when dealing with performance, security, and private data. SvelteKit is designed to make this separation clear and manageable.&lt;/p&gt;

&lt;p&gt;When your application is ready, the framework “builds” it for deployment. This process bundles and optimises your code so that the deployed webapp is fast, compact, and efficient.&lt;/p&gt;

&lt;p&gt;Many web frameworks exist, including React, Vue, and Angular. SvelteKit is a newer option, and I’m using it here because it offers a clean mental model, especially suited to beginners.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Svelte&lt;/em&gt; is the language that extends JavaScript and defines how components behave. &lt;em&gt;SvelteKit&lt;/em&gt; is the framework that provides routing, server integration, and the overall application structure. You’ll be using both together throughout this course.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.6 Firebase and App Engine
&lt;/h4&gt;

&lt;p&gt;You can develop a complete webapp on a modest desktop computer. However, once you want other people to use it, you’ll need a specialist &lt;em&gt;backend&lt;/em&gt; platform to support it on the web. This backend will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provide a public &lt;strong&gt;endpoint&lt;/strong&gt; (a URL) from which your webapp’s files are delivered to users’ browsers.&lt;/li&gt;
&lt;li&gt;Provide &lt;strong&gt;central storage&lt;/strong&gt; for your application’s data, typically in a structured database.&lt;/li&gt;
&lt;li&gt;Provide a &lt;strong&gt;secure environment&lt;/strong&gt; for running sensitive or privileged parts of your application logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this course, you’ll use two services from Google Cloud:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Firebase&lt;/strong&gt; — an application development platform that provides tools such as &lt;strong&gt;Firestore&lt;/strong&gt;, a simple and scalable cloud database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;App Engine&lt;/strong&gt; — a hosting platform used to deploy your SvelteKit webapp and run its &lt;strong&gt;server-side&lt;/strong&gt; code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because Firestore is used from the outset, your first step will be to create a Firebase account.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;In the past, cloud services were often provided by traditional Internet Service Providers such as GoDaddy or HostPapa. These services still have their place and can offer excellent hands-on support. For modern web application development, however, cloud platforms such as Google Cloud offer a more flexible, integrated toolset.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Now Read on
&lt;/h3&gt;

&lt;p&gt;This course is organised into two main stages.&lt;/p&gt;

&lt;p&gt;In the first stage, you’ll learn the foundations: &lt;strong&gt;HTML, JavaScript, Firebase, and Firestore&lt;/strong&gt;. If you’re new to web development, there’s a lot to absorb here, and it’s normal to feel unsure at times. Take it slowly, revisit sections as needed, and make active use of your chatbot tutor to unblock yourself.&lt;/p&gt;

&lt;p&gt;The second stage introduces more advanced &lt;strong&gt;SvelteKit&lt;/strong&gt; concepts and patterns. By the time you complete it, you’ll have the core skills needed to design, build, and deploy a genuinely useful web application.&lt;/p&gt;

&lt;p&gt;The next step is to install SvelteKit on your local machine and get comfortable with the webapp development workflow. See &lt;a href="https://dev.to/mjoycemilburn/ngsysv2-21-installing-sveltekit-and-creating-a-simple-all-html-webapp-3e5h"&gt;Post 2.1&lt;/a&gt; for instructions.&lt;/p&gt;

&lt;p&gt;Keep smiling! This is all going to be just fine!&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>svelte</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>NgSysV2-2.1: Installing Sveltekit and creating a simple all-HTML webapp</title>
      <dc:creator>MartinJ</dc:creator>
      <pubDate>Tue, 26 Nov 2024 08:12:46 +0000</pubDate>
      <link>https://dev.to/mjoycemilburn/ngsysv2-21-installing-sveltekit-and-creating-a-simple-all-html-webapp-3e5h</link>
      <guid>https://dev.to/mjoycemilburn/ngsysv2-21-installing-sveltekit-and-creating-a-simple-all-html-webapp-3e5h</guid>
      <description>&lt;p&gt;&lt;em&gt;This post series is indexed at &lt;a href="https://ngatesystems.com" rel="noopener noreferrer"&gt;NgateSystems.com&lt;/a&gt;. You'll find a super-useful keyword search facility there too.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Last reviewed: Aug'25&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Introduction
&lt;/h3&gt;

&lt;p&gt;This post is designed to give you a first taste of the realities of life as a webapp developer. It concludes with a simple working webapp but must start with the tricky "installation" tasks you must negotiate to get to the point where you can run this. &lt;/p&gt;

&lt;p&gt;While this is a nuisance, stick with it. Once you've got these out of the way, you'll have the chance to use Microsoft's VSCode editor to build a tiny piece of HTML. Finally, you'll use a VSCode terminal session to launch this in the browser via the Sveltekit local server.&lt;/p&gt;

&lt;p&gt;Does this sound good? Then read on.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Installation
&lt;/h3&gt;

&lt;p&gt;Absolute beginners might find this section a bit challenging, as it includes some procedures that aren't very user-friendly. However, it won't take long to get through, and you'll learn some basic skills that you will use frequently in the future. Here’s what you will do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a Project &lt;/li&gt;
&lt;li&gt;Install VSCode and open your Project within it&lt;/li&gt;
&lt;li&gt;Open a terminal session&lt;/li&gt;
&lt;li&gt;Install Node.js and npm&lt;/li&gt;
&lt;li&gt;Install SvelteKit.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  2.1 Creating a Project
&lt;/h4&gt;

&lt;p&gt;Your first step is to use the Windows File Explorer tool to create a new project. You'll need to choose a name for this. I suggest you use lowercase letters and hyphens and keep the name short. Something like &lt;code&gt;svelte-dev&lt;/code&gt; might be a good idea. You should also avoid locating your folder in Dropbox or OneDrive versioned storage. Webapp projects tend to become quite bulky and are liable to overwhelm general-purpose versioning systems. In any case, VSCode and Git will provide all the versioning you need.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.2 Installing VSCode and adding your Project to its "workspace"
&lt;/h4&gt;

&lt;p&gt;Follow the instructions at &lt;a href="https://code.visualstudio.com/docs/introvideos/basics" rel="noopener noreferrer"&gt;Getting Started with Visual Studio Code&lt;/a&gt; to install the software on your device. Launch it using its desktop icon and note the "File" entry in the menu bar at the top of VSCode's screen. Click this, select "Add Folder to Workspace", navigate to your project folder location and select/add it. &lt;/p&gt;

&lt;p&gt;At this point, the VSCode screen will display three columns of information below its menu bar: a toolbar, an "explorer" panel showing "workspace" content details and a large blank area waiting for you to edit workspace files. &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%2Fp10vd0uhrt675kus6rfw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp10vd0uhrt675kus6rfw.jpg" alt="Basic VSCODE screen layout" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You may wonder why VSCode burdens you with a "workspace" concept when you only have a single project. This is because, over time, you'll accumulate numerous projects and find that you want to share code between them. The "workspace" allows you to operate on a group of projects. You can remove a project from the workspace by right-clicking on it and selecting "Remove Folder from Workspace". If you close and restart VSCode you'll find that it has remembered your previous workspace content setting and will reinstate it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Since you will be using VSCode a lot you might consider "pinning" VSCode to your desktop toolbar. Note that, once you've added a project to a workspace, VSCode will hang on to it between restarts until you explicitly remove the folder with a r-click "remove folder from workspace" command.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.3 Opening a terminal session in VSCode
&lt;/h4&gt;

&lt;p&gt;You can open a new terminal session on your project by selecting "Terminal -&amp;gt; New Terminal" on the VSCode menu and then clicking your project's entry in the list that appears. If the "Terminal" option isn't visible on the menu bar, you'll find it in the "..." overspill area.&lt;/p&gt;

&lt;p&gt;The "terminal" should appear as a sub-window at the bottom of the editing area on the right-hand side of the  VSCode screen. Its height and width can be resized by clicking and dragging on the top and left borders. You'll find that you can also toggle it on or off using the ctrl' shortcut trick again. Currently, it is now waiting for you to enter "terminal session" commands on a line preceded by the full address of your project folder. &lt;/p&gt;

&lt;p&gt;If your experience of IT to date has been entirely through the use of "click and point" on Windows (Microsoft) and iOS (Mac)screens, the heavy reliance in this post on the use of  "terminal" sessions may come as an unwelcome shock.&lt;/p&gt;

&lt;p&gt;The word "terminal" here takes you back to the early days of computing, long before the appearance of the "graphical user interfaces" that we use today. Developers then used "terminal" devices such as teletypes or "VDU" visual display units to issue operating system instructions by typing "commands" rather than by clicking buttons. These "command shell" interfaces still have great advantages for system programmers because they're easy to set up and are highly flexible. Terminal sessions thus continue to be widely used in the development process. Naturally, if you've been brought up on "point and click" interfaces, you will see them as a backward step. But you'll quickly find that they work well and will, in any case, add another useful building block to your growing range of development skills. &lt;/p&gt;

&lt;p&gt;One could write a book about terminal commands but, for now, you only need to know a few basic facts. Firstly, you'll find that you can't use the mouse to edit a terminal command. If you spot an error in the middle of a command, you must use the "Backspace" key to remove subsequent content and retype it. But this inconvenience is offset by the fact that the default Powershell terminal session used in VSCode keeps a history of the commands you use and allows you to reference a previous command by pressing the up-arrow key repeatedly until the one you want pops up. The down-arrow key reverses the process. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI-powered tools such as &lt;a href="https://www.warp.dev/blog/launching-warp-on-windows" rel="noopener noreferrer"&gt;Warp&lt;/a&gt; now look set to displace the historically "geeky" nature of parameterised terminal commands with conversational English. I could get used to this!&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  2.4 Installing Node.js and npm
&lt;/h4&gt;

&lt;p&gt;You may already be feeling a little tired, but stick with it. You're nearly at a point where you can start using SvelteKit. &lt;/p&gt;

&lt;p&gt;The "problem" is that SvelteKit is distributed over the Internet as a "package". Installation of the SvelteKit package requires a "package manager". What is a package and what is a package manager?&lt;/p&gt;

&lt;p&gt;A "package" is a neatly bundled collection of code files carefully stamped with version numbers and accompanied by details of its dependencies on other packages. Modern software development practices use hierarchies of packages to share useful code components.&lt;/p&gt;

&lt;p&gt;A "package manager" is a tool that enables you to "install" a package into a project by unpacking its code content and copying it into project folders. The package manager checks compatibility with any other packages that may have been installed. &lt;/p&gt;

&lt;p&gt;The package manager you'll use here is called npm (Node Package Manager). Package management is demanding, so alternative managers are available to meet specialist situations. But the npm package manager is the standard choice and is recommended here. You'll use npm repeatedly during the development of a project as you find the need to include additional components.&lt;/p&gt;

&lt;p&gt;Jumping ahead a little, once the npm package manager and its run-time environment have been installed, you can use it to install a "my-package" package into your project. You do this by launching a command like &lt;code&gt;npm create my-package&lt;/code&gt; in a VSCode terminal session. This downloads the "my-package" library files into your project's &lt;code&gt;node-modules&lt;/code&gt; folder. &lt;/p&gt;

&lt;p&gt;But there's a snag: npm requires a "Node.js runtime environment," which must also be installed. &lt;/p&gt;

&lt;p&gt;The good news is that, for Windows users at least, Node.js is installed by downloading and opening a standard &lt;code&gt;msi&lt;/code&gt; (Microsoft Installation) file. Even better, the installation procedure thus launched lets you install npm as well.&lt;/p&gt;

&lt;p&gt;However, running the procedure is quite a challenge for a beginner. The basic download arrangement is documented at &lt;a href="https://nodejs.org/en" rel="noopener noreferrer"&gt;https://nodejs.org/en&lt;/a&gt; at  &lt;a href="https://nodejs.org/en" rel="noopener noreferrer"&gt;Run JavaScript Everywhere&lt;/a&gt; but this is very light on operating instructions. You might find it helpful to check out the more detailed instructions at &lt;a href="https://phoenixnap.com/kb/install-node-js-npm-on-windows" rel="noopener noreferrer"&gt;How to Install Node.js and NPM on Windows&lt;/a&gt;. Take your time with this. If things go wrong, all you have to do is uninstall Node.js and start again. The best advice I can give you is to use the standard default settings offered by the procedure - overrides are strictly for experts.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.5 Installing SvelteKit
&lt;/h4&gt;

&lt;p&gt;At last, you are in a position to make some progress! When you complete this stage, you'll be rewarded by a simple demo SvelteKit page running in your browser.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open a terminal session in VSCode for your project, as before, and run the following &lt;code&gt;npx&lt;/code&gt; command:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;npx&lt;/span&gt; &lt;span class="nx"&gt;sv&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As of November 2024, this launches a SvelteKit dialogue that walks you through configuring your project with Svelte 5. The &lt;code&gt;px&lt;/code&gt; tool employed here is a "package runner" tool embedded within &lt;code&gt;npm&lt;/code&gt;—it's installed automatically with &lt;code&gt;npm&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;As an aside, be assured that if things go wrong with this procedure and you want to start again, all you have to do is delete the entire content of your project folder and have another try (click the gear icon on the Firebase Console page for the project, select Project Settings, scroll down to the "Shut down" entry and proceed as directed). Note also that npm installations often display worrying lists of warning messages as they check for possible incompatibilities in the files they are building into your project. It's very unlikely that these will be relevant to you as a beginner so I suggest you simply ignore them.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Svelte's first question is, "Where should we create your project?" Since your terminal session is already open in your project folder, you can press the return key here.&lt;/p&gt;

&lt;p&gt;Now, Svelte wants to know what style of project it should create. I'd like you to select "minimal" here. The procedure for doing this in a terminal session is slightly awkward because you can't use the mouse to toggle a checkbox as you would do on a web page. You indicate your preference here by using the down arrow key to position yourself on the "minimal" line and then pressing the return key&lt;/p&gt;

&lt;p&gt;And now Svelte wants to know if you want to use TypeScript checking features. This advanced Javascript extension will be critical to you when you're working on a complex production-grade webapp (it polices your use of variable types in Javascript and ensures consistency). But you don't need this here, and your learning curve is already becoming exponential, so I suggest you select "No" by pressing the down arrow twice and then pressing the return key.&lt;/p&gt;

&lt;p&gt;Then, Svelte wonders if you'd like to include "additional options". The ESLint syntax-checking tool shown at the top of the list presented can be helpful. This can sometimes be a "noisy" nuisance, warning you about issues you're not especially interested in. But, on balance, I suggest you accept it here by pressing the space bar and the return key. &lt;/p&gt;

&lt;p&gt;Finally, Svelte asks which package manager you want to use. Select &lt;code&gt;npm&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can probably safely ignore any other options that might be presented.&lt;/p&gt;

&lt;p&gt;Svelte is now all geared up to build your project. The curious display you now see played out on your screen may be your first sight of npm downloading package files into a project. You might find it instructive to note that the svelte-dev folder in your workspace window has suddenly blossomed with an impressive display of sub-folder content. This is the code that represents your "minimal" SvelteKit project.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;It's not always the case that package software ends up in your project. Where you're installing a tool that might be useful shared with other projects, you can request that it be installed "globally". You do this by adding a "-g" "flag field" to your npm command. You'll see flag fields used widely in terminal commands. For now, though, leave your installer software to give you guidance on their use. Ask chatGPT to give you a tutorial on this sometime.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The Svelte installer now asks you to run &lt;code&gt;npm run dev -- --open&lt;/code&gt; (forget any reference to &lt;code&gt;git&lt;/code&gt; at this stage - you'll learn all about this later in &lt;a href="https://dev.to/mjoycemilburn/ngsysv2-52-git-for-beginners-4l0e"&gt;Post 5.2&lt;/a&gt;). Give the command a try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run dev -- --open
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This launches the SvelteKit local server and runs your project within it. The local server has the job of creating "localhost" pages on port 5173 in your browser. If this sentence makes no sense to you,  you will now &lt;strong&gt;see&lt;/strong&gt; what it means anyway because the "-- --open" bit of the npm command automatically hands control to your browser and gives you a demonstration. Your screen should look something like the picture below:&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%2F53mqnlilu6su7xcjlrlt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F53mqnlilu6su7xcjlrlt.jpg" alt="Svelte " width="800" height="293"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a webapp coded with exactly the same sort of HTML that you saw in &lt;a href="https://dev.to/mjoycemilburn/ngsysv2-11-a-young-persons-guide-to-systems-development-in-the-age-of-ai-39pe"&gt;Post 1.1&lt;/a&gt;. The difference is that its content is being monitored by the SVelteKit framework. The code is currently sitting in your new VSCode &lt;code&gt;svelte-dev&lt;/code&gt; project. Let's find the bit that displays the "Welcome to SvelteKit" banner on your browser screen, change it and see what happens. &lt;/p&gt;

&lt;p&gt;Back in VSCode, with your project visible in its "workspace", use its folder/file hierarchy to navigate its content exactly as you would in Windows Explorer. You'll find that the project has acquired quite an impressive collection of code! For example, there's now a &lt;code&gt;node_modules&lt;/code&gt; folder packed with tiny files. This is where all those files downloaded during your &lt;code&gt;npm install&lt;/code&gt; work went. Now locate the &lt;code&gt;+page.svelte&lt;/code&gt; file that you'll find in the &lt;code&gt;src/routes&lt;/code&gt; folder and click it. (The significance of its name and location within the src folder will be clarified in future posts - let's simply say for now that the name "+page.svelte" is rather important). You'll now see the contents of &lt;code&gt;+src/routes/+page.svelte&lt;/code&gt; displayed in the VSCode edit window on the RHS of the screen. The &lt;code&gt;&amp;lt;h1&amp;gt;Welcome to SvelteKit&amp;lt;/h1&amp;gt;&lt;/code&gt; line here should look familiar - yes this is the bit of HTML that displays the heading. Now change the line to read &lt;code&gt;&amp;lt;h1&amp;gt;Hello there&amp;lt;/h1&amp;gt;&lt;/code&gt; Save the file (using the usual ctrl S shortcut) and look at your browser again. Wow, the "Welcome to SvelteKit" message has been replaced by your "Hello there" message.&lt;/p&gt;

&lt;p&gt;Because the SvelteKit server you started with &lt;code&gt;npm run dev&lt;/code&gt; has been monitoring your project folder, whenever you change a file, the server automatically transmits the changes to the active application in the browser.&lt;/p&gt;

&lt;p&gt;This is going to make development so much fun!&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Summary
&lt;/h3&gt;

&lt;p&gt;If you've survived this post give yourself a gold star. You've managed to get a SvelteKit webapp running in development mode and, just as importantly, have created the VSCode, npm and Node.js "scaffolding" that will get every future SvelteKit project off to a flying start.&lt;/p&gt;

&lt;p&gt;The next post in this series will show you how to use Javascript and the Svelte "language" to add intelligence to your webapp.&lt;/p&gt;

&lt;h3&gt;
  
  
  Postscript 1: When things go wrong
&lt;/h3&gt;

&lt;p&gt;It's not difficult to get in a mess with this stuff - the most experienced developers will sometimes mistype the name of an HTML tag and flood their screen with fearsome error messages. The difference is that they've seen it all before and know not to panic! As a beginner, when you see this sort of thing, you'll probably think you've completely wrecked your computer and must now buy a new one. Calm down. You can sort this.&lt;/p&gt;

&lt;p&gt;Errors will be signalled in many different ways. Syntax errors in a &lt;code&gt;.js&lt;/code&gt; (javascript) file open in VSCode will be flagged in the edit window. In the example below I've deliberately crippled the HTML code for the header message in a "minimal" project by dropping the closing &lt;code&gt;&amp;gt;&lt;/code&gt; of the initial &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; tag. &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%2Feuh2iz2i68u57ywqkztr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feuh2iz2i68u57ywqkztr.jpg" alt="Demonstration of VSCode error-signalling" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The erroring file and its parent folders have all been highlighted in red in the folder hierarchy. This means the code is broken and will throw an error message if you run it. Try this out - you'll find that the localhost page for your webapp will display a "500 Internal Error" when you refresh it. Back in your VSCode terminal window, a mass of error details will also have been generated by the SvelteKit server when you saved the errored file.&lt;/li&gt;
&lt;li&gt;The position of the "error" has been underlined in the edit window. When you mouse over this you will get a tooltip that gives you error details&lt;/li&gt;
&lt;li&gt;A "count" of the total number of errors and warnings in your &lt;code&gt;svelte-dev&lt;/code&gt; project is displayed in the blue "status" bar at the bottom of the VSCode page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a perfect example of why you need to stay cool. Behind all this mayhem lies a perfectly trivial error&lt;/p&gt;

&lt;p&gt;In the first place, you'll see that this doesn't all add up. The system incorrectly highlights the concluding &lt;code&gt;&amp;lt;/h1&amp;gt;&lt;/code&gt; tag as the source of the error. The real problem lies in the broken opening &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; tag. Once you've seen this a few times, you'll automatically think "Oh, there's something wrong with the tag pattern". Learn to hang loose. At least the system identified the correct line.&lt;/p&gt;

&lt;p&gt;My general practice is that, when things go wrong with my code, I start by start fixing problems any highlighted by the editor. But there &lt;strong&gt;will&lt;/strong&gt; be times when VSCode is perfectly happy but your browser is displaying an error. This is when you must look at the error messages displayed in your terminal session. These errors will be more serious and my advice is to take a deep breath, read the messages closely and try to think through what it is trying to tell you. If you're still stuck, paste the error message into chatGPT and ask for advice. Google searches citing Stackoverflow are also a good resource.&lt;/p&gt;

&lt;p&gt;If you're &lt;strong&gt;still&lt;/strong&gt; stuck, take a break, go for a walk and get some fresh air. That can make a huge difference. The great thing about computing is that, when things go wrong, there's always a reason. Even better, when you find that reason and fix it, it &lt;strong&gt;stays&lt;/strong&gt; fixed. Consider yourself lucky - you could be trying to fix a problem involving people, where the exact reverse applies! &lt;em&gt;Courage, mon brave&lt;/em&gt;. You'll find that coding problem and fix it, for sure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Postscript 2: Working with VSCode
&lt;/h3&gt;

&lt;p&gt;The VSCode Edit panel may feel a bit restricted when the VSCode is also displaying the Explorer panel. You might like to know that you can toggle the Explorer panel off and on by clicking the Explorer icon at the top left of the toolbar column.&lt;/p&gt;

&lt;p&gt;Similarly, remember that when the Edit panel is overlayed by a terminal session, you can quickly toggle this off with the "ctrl ' " shortcut.&lt;/p&gt;

&lt;p&gt;Finally, you will find it useful to know that multiple terminal windows can be active simultaneously. In this case, the menu bar of the terminal window displays a list of active terminals and enables you to switch between them by clicking on list entries. A "dustbin"  icon here also lets you kill a window, though this may only be visible if you've made the terminal window big enough- space is always at a premium in VSCode.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>svelte</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>NgSysV2-2.2: Creating a simple Reactive Svelte webapp</title>
      <dc:creator>MartinJ</dc:creator>
      <pubDate>Tue, 26 Nov 2024 08:06:26 +0000</pubDate>
      <link>https://dev.to/mjoycemilburn/ngsysv2-22-creating-a-simple-reactive-svelte-webapp-39jc</link>
      <guid>https://dev.to/mjoycemilburn/ngsysv2-22-creating-a-simple-reactive-svelte-webapp-39jc</guid>
      <description>&lt;p&gt;&lt;em&gt;This post series is indexed at &lt;a href="https://ngatesystems.com" rel="noopener noreferrer"&gt;NgateSystems.com&lt;/a&gt;. You'll find a super-useful keyword search facility there too.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Last reviewed: Feb '25&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Introduction
&lt;/h3&gt;

&lt;p&gt;The previous post created a Svelte webapp made up entirely of simple HTML. It didn't contain any Javascript and thus didn't use any of Svelte's sophisticated dynamic HTML generation facilities. This post will only scratch the surface of Svelte features but should give you a feel for the power of the language.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. State and Reactivity
&lt;/h3&gt;

&lt;p&gt;Viewed objectively, a webapp is a complex hierarchy of HTML pages, popups, tables, forms and buttons. This dizzying assembly of visual elements is "informed" by &lt;strong&gt;data&lt;/strong&gt;.  Tables are inflated with JavaScript array content. Popups appear and vanish at the behest of flag fields. Forms blossom with data when "poked" by mouse and keyboard input. In short - it's complicated. How can some sort of order be established over this situation?&lt;/p&gt;

&lt;p&gt;Collectively, the body of data defining the appearance of a webapp at any moment in time is referred to as the webapp's &lt;strong&gt;state&lt;/strong&gt;. Much effort has been expended on software development technology designed to make it easier to write webapps that &lt;strong&gt;react&lt;/strong&gt; efficiently when state changes. Platforms like Svelte are expressly designed to deliver good reactivity.&lt;/p&gt;

&lt;p&gt;Let's look at Svelte's approach to the definition of State and its implementation of Reactivity.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.1 A simple reactive webapp
&lt;/h4&gt;

&lt;p&gt;A common feature on a web page is a pop-up window that is displayed (usually annoyingly) when you start it up, but which then, obligingly, disappears when you click on it. Let's see how Svelte would let you use State and Reactivity to implement this in a webapp.&lt;/p&gt;

&lt;p&gt;In Javascript, you need to define a variable before you can use it. So for example, a command like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;myDataItem&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;myDataItem&lt;/span&gt;  &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;will throw an error unless somewhere earlier in the code you had defined it using a statement like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;myDataItem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;As an aside, you'll find it useful to have access to a "playground" that lets you quickly try out bits of JavaScript like this. The "inspect" view of any browser page provides one such playground - open the inspector on a random page and select the "console" tab on its menu bar. Alternatively, you might try the javascript console at &lt;a href="https://playcode.io/javascript" rel="noopener noreferrer"&gt;PlayCode&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There's lots to say about JavaScript's &lt;code&gt;let&lt;/code&gt; keyword (try asking chatGPT about "scope") but &lt;code&gt;let&lt;/code&gt; has an additional significance in Svelte because this is how you define State. In a Svelte program, any variable defined with &lt;code&gt;let&lt;/code&gt; becomes part of the program's State.&lt;/p&gt;

&lt;p&gt;So what? Well, I said earlier that State variables - variables that define the appearance of a webapp - are "reactive". This means that when their value changes, the appearance of the webapp is directed automatically to change accordingly.  Suppose you use a &lt;code&gt;popupVisible&lt;/code&gt; boolean variable to define the state of the popup. Could Svelte - a reactive platform - use the variable's value to determine whether or not the popup is visible? Let's try.&lt;/p&gt;

&lt;p&gt;Here's the code I propose to use. I'll explain what it does in a minute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//src/routes/+page.svelte - remove this line before running&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;popupVisible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;togglePopup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;popupVisible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;popupVisible&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;popupVisible&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;
            &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;height: 6rem; width: 25rem; background-color: blue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;togglePopup&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sr"&gt;/if&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code here is divided into two sections. The upper "script" section, defined by the &lt;code&gt;&amp;lt;script&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt; tags, declares a &lt;code&gt;popupVisible&lt;/code&gt; javascript variable and a &lt;code&gt;togglePopup()&lt;/code&gt; function. The lower "template" section, specifies HTML "moderated" by a Svelte {#if} {/if} logic block. Svelte code can reference variables and functions defined in the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; section to guide HTML code generation. References are enclosed by curly &lt;code&gt;{}&lt;/code&gt; brackets, except when they're used directly within a logic block - see Svelte docs at &lt;a href="https://svelte.dev/docs/basic-markup" rel="noopener noreferrer"&gt;Basic Markup&lt;/a&gt; and &lt;a href="https://svelte.dev/docs/logic-blocks" rel="noopener noreferrer"&gt;Logic Blocks&lt;/a&gt; for full details.&lt;/p&gt;

&lt;p&gt;The code displayed above is very crude. A "pop-up" is normally defined by a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; tag formatted with a border and some positioning CSS. Here I've used a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; tag - something that would be more commonly used to define the "submit" element on a form. I've done this simply because it enables me to avoid some technicalities that might distract you. I've coloured the button "popup"  blue so that you can see it.&lt;/p&gt;

&lt;p&gt;If you still have a copy of the skeleton svelte-dev Svelte project created in &lt;a href="https://dev.to/mjoycemilburn/ngsysv2-21-installing-sveltekit-and-creating-a-simple-all-html-webapp-3e5h"&gt;Post 2.1&lt;/a&gt;, try using this code to replace the entire content of &lt;code&gt;src/routes/+page.svelte&lt;/code&gt;. When you open a terminal on the project and start the &lt;code&gt;dev&lt;/code&gt; server as before, your browser should now leap into life and display the screen shown below:&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%2F9zvxqymbfxwnasx3np67.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9zvxqymbfxwnasx3np67.jpg" alt="Graphic to illustrate Svelte reactivity" width="800" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The blue rectangle is the "pop-up". It is displayed at startup because at this point the &lt;code&gt;popupVisible&lt;/code&gt; variable is set to true and the Svelte logic driving the page's HTML generation is instructed to create a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; tag in this case.&lt;/p&gt;

&lt;p&gt;Now try clicking the pop-up. Magic! It disappears. This is because the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; tag specifying the popup contains an &lt;code&gt;onClick&lt;/code&gt; clause that makes a click on the element run the &lt;code&gt;togglePopup&lt;/code&gt; function. This in turn sets the &lt;code&gt;popupVisible&lt;/code&gt; variable to &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I said earlier that changing a Svelte "state" variable causes Svelte to refresh the screen by re-running the page's template section. So, the template section re-runs and now, since the value of &lt;code&gt;popupVisible&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt;, the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; code displaying the pop-up is bypassed leaving an empty page.&lt;/p&gt;

&lt;p&gt;Take a moment now to let this sink in. If you'd still been working with the primitive DOM-manipulation code I introduced in &lt;a href="https://dev.to/mjoycemilburn/ngsysv2-11-a-young-persons-guide-to-systems-development-in-the-age-of-ai-39pe"&gt;Post 1.1&lt;/a&gt; this effect could only have been achieved by writing a good deal of fairly technical (and probably rather inefficient) low-level code. Here, you've just had to change the value of a variable. Sveltekit has handled the intricate consequences &lt;strong&gt;automatically&lt;/strong&gt; (and, rest assured, efficiently). &lt;/p&gt;

&lt;p&gt;In short, Svelte gives you a high-level language that lets you leave the tedious details of browser screen management to the framework.  &lt;/p&gt;

&lt;p&gt;&lt;em&gt;In October '24, Svelte 5 introduced a series of refinements to its reactivity-definition arrangements. The &lt;code&gt;let&lt;/code&gt; construct described above still works fine for everything in this post series, but a new &lt;code&gt;runes&lt;/code&gt; concept is now available to handle more complex requirements. See &lt;a href="https://svelte.dev/docs/svelte/what-are-runes" rel="noopener noreferrer"&gt;What are Runes&lt;/a&gt; for details.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  2.2 Data Input
&lt;/h4&gt;

&lt;p&gt;Let's make the pop-up a bit more interesting. Imagine that you had the job of maintaining a "products" list for a manufacturing company. You'd want a utility that displays the current list and enables you to add new products. In practice, of course, you'd also want it to be able to edit and delete entries, but let's keep things simple for now. &lt;/p&gt;

&lt;p&gt;Have a look at the following code. It will probably stretch your JavaScript knowledge but the internal comments should help.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//src/routes/+page.svelte - remove this line before running&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;popupVisible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;newProductNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt; &lt;span class="c1"&gt;// An array of product objects {productNumber: productNumber}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-align: center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Inventory&lt;/span&gt; &lt;span class="nx"&gt;Maintenance&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;popupVisible&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c"&gt;&amp;lt;!--&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;popup&lt;/span&gt; &lt;span class="nx"&gt;not&lt;/span&gt; &lt;span class="nx"&gt;visible&lt;/span&gt;&lt;span class="o"&gt;--&amp;gt;&lt;/span&gt;

        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Currently&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;registered&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="nx"&gt;Numbers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c"&gt;&amp;lt;!--&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;  &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="o"&gt;--&amp;gt;&lt;/span&gt;

            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;margin: 0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;productNumber&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sr"&gt;/each&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;
            &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;margin-top: 1rem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// a function to toggle the popup on&lt;/span&gt;
                &lt;span class="nx"&gt;popupVisible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}}&lt;/span&gt;
            &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Add&lt;/span&gt; &lt;span class="nx"&gt;Another&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
    &lt;span class="p"&gt;{:&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;   &lt;span class="c"&gt;&amp;lt;!--&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="nx"&gt;registration&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="o"&gt;--&amp;gt;&lt;/span&gt;

        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;border:1px solid black; height: 6rem; width: 30rem; margin: auto;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="nx"&gt;Registration&lt;/span&gt; &lt;span class="nx"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;
                    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;newProductNumber&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/label&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;
                    &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                    &lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="c1"&gt;// a function to add the product number to the array and hide the popup&lt;/span&gt;
                        &lt;span class="nx"&gt;products&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="na"&gt;productNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newProductNumber&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
                        &lt;span class="nx"&gt;popupVisible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                        &lt;span class="nx"&gt;newProductNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Clear the input field&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;Register&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/form&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sr"&gt;/if&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "template" section here begins by checking the value of &lt;code&gt;popupVisible&lt;/code&gt;. If this is &lt;code&gt;false&lt;/code&gt; (which it will be on startup because the variable has just been initialised in the "script" section), the webapp displays the webapp's current list of "products" (an array of &lt;code&gt;productNumbers&lt;/code&gt; stored in a &lt;code&gt;products&lt;/code&gt; array). &lt;/p&gt;

&lt;p&gt;The code now move on and displays an "Add another Product"&lt;code&gt;button&lt;/code&gt; - a normal one now, not the freakish version used in the previous example. But, as before, it has an associated &lt;code&gt;on:click&lt;/code&gt; function, and this one sets the value of &lt;code&gt;popupVisible&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What will happen if the button is clicked? Why, since &lt;code&gt;popupVisible&lt;/code&gt; is a reactive variable, the webapp will refresh its screen.&lt;/p&gt;

&lt;p&gt;This time, the products-display section of the form is ignored and control will move straight to the second part of the template section.&lt;/p&gt;

&lt;p&gt;Here, you'll see something more like a conventional popup. This one uses a &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; tag within a container &lt;code&gt;div&amp;gt;&lt;/code&gt; to collect input for a &lt;code&gt;newProductNumber&lt;/code&gt; state variable.  A special Svelte "bind" qualifier" is employed here here to synchronize &lt;code&gt;newProductNumber&lt;/code&gt; with the user's keyboard typing. Every time the user types a character into the form's &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; field the &lt;code&gt;newProductNumber&lt;/code&gt; is updated. &lt;/p&gt;

&lt;p&gt;When the user finally clicks the form's "Register" button, its &lt;code&gt;on:click&lt;/code&gt; function is thus able to push a fully-updated &lt;code&gt;newProductNumber&lt;/code&gt; into the &lt;code&gt;products&lt;/code&gt; array and reset the &lt;code&gt;popupVisible&lt;/code&gt; field.&lt;/p&gt;

&lt;p&gt;Finally, since &lt;code&gt;popupVisible&lt;/code&gt; is a reactive variable, the "template" section of the code re-runs and the popup is replaced by the updated list of product numbers.&lt;/p&gt;

&lt;p&gt;One or two other things may puzzle you. In the previous example, an&lt;code&gt;on:click&lt;/code&gt; qualifier referenced a function defined in the webapp's &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; section. In this new version of &lt;code&gt;+page.svelte&lt;/code&gt;, however, the function is defined explicitly within the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; field's HTML spec. Both approaches are perfectly valid. The version used here probably looks a bit strange to you, but it is a widely-used arrangement that has the advantage that the function's logic is defined at its point of invocation. This makes it much easier to see what's going on. &lt;/p&gt;

&lt;p&gt;For reference, the expression:&lt;br&gt;
"() =&amp;gt; { .... }" is saying "here's a function defined by the following JavaScript statements: "{ .... }". There are many variants. Ask chatGPT to give you a tutorial on "arrow" functions .&lt;/p&gt;

&lt;p&gt;Moving on, you'll also note that I've been much more adventurous with my "styling". There's a heading, for example, and everything is nicely centred (CSS "inheritance" ensures that the &lt;code&gt;style="text-align: center"&lt;/code&gt; of the enclosing &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; is also applied to everything within the &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; block). Then there's the horrific &lt;code&gt;style="border:1px solid black; height: 6rem; width: 30rem; margin: auto;"&lt;/code&gt; attribute applied to the  &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; tag enclosing the Registration form. Ask chatGPT for a description of what this does. A tutorial on the "border box" model might be handy too.  &lt;/p&gt;

&lt;p&gt;But that's enough background. Let's see the webapp in action. Paste the new code over the current content of your &lt;code&gt;+page.svelte file&lt;/code&gt;, save it and (if necessary) open a terminal on your project and restart your dev server with the &lt;code&gt;npm run dev -- --open&lt;/code&gt; command. Here's what you should see in your browser::&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%2Ftairpun5y8ozwhdf1p5u.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftairpun5y8ozwhdf1p5u.jpg" alt="Graphic showing the appearance of the Inventory Maintenance Home page" width="800" height="283"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here's what you should see when you click the button:&lt;br&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft8nq0u0w1gpkfwbur7pm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft8nq0u0w1gpkfwbur7pm.jpg" alt="Graphic showing the appearance of the Inventory Maintenance popup" width="800" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since there's no validation on the &lt;code&gt;newProductNumber&lt;/code&gt; field, you will find that you can enter characters as well as numbers - we'll fix this in a future post. Even so, when you click the "Register" button, you should see the popup replaced by the original Inventory display page, with your new product "number" added to the list.&lt;/p&gt;

&lt;p&gt;This scrap of Svelte is almost starting to look like an information system! &lt;/p&gt;

&lt;h3&gt;
  
  
  3. Onward and Upward
&lt;/h3&gt;

&lt;p&gt;This post has given you a brief glimpse of the key concepts embodied in the Svelte language. Unfortunately, there's a snag. As I'm sure you will have noticed, the "Inventory" example you've just seen is effectively useless because every time you close the webapp its product list disappears! The next step, therefore, is to introduce you to Google's &lt;strong&gt;Firestore&lt;/strong&gt; database technology. This will let you create &lt;strong&gt;persistent&lt;/strong&gt; storage on a server host. &lt;/p&gt;

&lt;h3&gt;
  
  
  Postscript (a): When things go wrong - Investigating Layout issues with the Chrome Inspector
&lt;/h3&gt;

&lt;p&gt;"When things go wrong" in &lt;a href="https://dev.to/mjoycemilburn/ngsysv2-21-installing-sveltekit-and-creating-a-simple-all-html-webapp-3e5h"&gt;Post 2.1&lt;/a&gt; described how to handle some of the grosser problems you'll encounter when developing a webapp. But now that you're working at a more advanced level you need a more sophisticated tool. This section introduces you to the "Chrome Inspector", an invaluable aid when correcting problems with your webapp's screen layout and logic errors in your JavaScript (and much more besides, as you'll see later in this series).&lt;/p&gt;

&lt;p&gt;The Inspector is launched on a webapp Page by right-clicking on the page and selecting the "Inspect" option. Here's an example of its output:&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%2Fxa5sy5ygwlidff8byy1m.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxa5sy5ygwlidff8byy1m.jpg" alt="Screenshot illustrating the use of the Chrome Inspector element-debugging tools" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this screenshot, I have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Launched the Inspector, as above. wThe Inspection Window is now hiding the bottom half of the webapp window. This can be inconvenient because the Inspector may now be hiding some webapp elements that I want to inspect! This is partially handled by "sizing" the inspector window by clicking and dragging on its upper border. Alternatively, you can the "Dock Side" tool under the triple-dot icon on the extreme right of the menu to dock the Inspector to the side of the screen.&lt;/li&gt;
&lt;li&gt;Selected the "Elements" tab from the Inspector's menu bar&lt;/li&gt;
&lt;li&gt;Expanded the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; lines in the HTML tag hierarchy displayed in the left-hand inspection panel and moused over the &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; tag showing the webapp's heading&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you try this yourself, you'll note that the heading has acquired some coloured graphics. These show you the size and position of the margin, border and padding that have been added to your text. The right-hand inspection panel provides more detail. If you click the &lt;code&gt;computed&lt;/code&gt; tab in its menu, you'll get a graphic that explains the colour coding and detailed sizings for the various elements. If you click the "styles" tab you'll see details of the styles attached to the text. The first set of entries in the list confirms those styles set explicitly with the tag's &lt;code&gt;style=&lt;/code&gt; clause (none in this case). After that, you'll see any styles inherited in other ways. &lt;/p&gt;

&lt;p&gt;The beauty of this panel is that you can use the inspector to see the effect of changing and adding styles. For example, suppose you wanted to try out a red font on the heading, click somewhere in the &lt;code&gt;element.style&lt;/code&gt; entry at the top of the panel and enter &lt;code&gt;color: red&lt;/code&gt;. The heading, obligingly, now turns red. Note the opportunity to use autocompletion to try out different colours.&lt;/p&gt;

&lt;p&gt;I don't want to labour the point, but I think you should be starting to see how this facility can give you a precise and intuitive tool for investigating and fixing layout issues. For more information check out Google's documentation on the inspector at &lt;a href="https://developer.chrome.com/docs/devtools" rel="noopener noreferrer"&gt;DevTools&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Be prepared to spend quite some time on this. The Inspector is a complex tool so it will be a while before you can use it confidently.  But this will be time well spent. The Inspector has certainly saved me countless hours of blind experimentation with source-code settings. The inspector's graphic display makes visible all the hidden properties of element widths, margins and padding.&lt;/p&gt;

&lt;h4&gt;
  
  
  Postcript (b): Investigating Logic issues with the Chrome Inspector
&lt;/h4&gt;

&lt;p&gt;The Inspector can also help you find logic errors by enabling you to view your source code and set "breakpoints" on lines where you would like execution to halt. After refreshing the webapp (with the Inspector still open), you can inspect field values at each breakpoint. Here's a  screenshot of the Inspector in action:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn2znfg8hyrl2allpojav.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn2znfg8hyrl2allpojav.jpg" alt="Screenshot illustrating the use of the Chrome Inspector's source-debugging tools" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this screenshot, I've:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Launched the Inspector as before. &lt;/li&gt;
&lt;li&gt;Clicked the "Sources" tab above the left-hand panel and expanded the source hierarchy for localhost in the page view to enable me to click on the italicised &lt;code&gt;+page.svelte&lt;/code&gt; entry for my route in the &lt;code&gt;src/routes&lt;/code&gt; line.&lt;/li&gt;
&lt;li&gt;Noted that the right-hand panel now displays the source for &lt;code&gt;+page.svelte&lt;/code&gt; and clicked on the &lt;code&gt;let popupVisible = false;&lt;/code&gt; line to set a breakpoint (confirmed by a blue highlight on its line number).&lt;/li&gt;
&lt;li&gt;Refreshed the page in the browser and noted that the webapp is now displaying a "paused in Debugger Message" that includes a "Resume execution" button and a "Step over next function call" button. You'll also see that the &lt;code&gt;let popupVisible = false;&lt;/code&gt; line  is now highlighted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The effect of all this has been to put the webapp into "code debug" mode. Right now, I'm debugging the &lt;strong&gt;&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;&lt;/strong&gt; section of the webapp, which is a bit pointless since it only runs once and you know exactly what it does. But let's go through the motions because they'll help you get a feel for what the debugger can do for you in JavaScript source code.&lt;/p&gt;

&lt;p&gt;Currently, the webapp is halted before execution of the first statement and so hasn't done anything useful. But advance to the next statement by clicking the "Step over next" button and note that the highlight has now moved to the second line. Mouse over the &lt;code&gt;popupVisible&lt;/code&gt; field in the first line and note that a tooltip is now displayed showing its current value - &lt;code&gt;false&lt;/code&gt;, of course. Repeat the trick to confirm the settings of &lt;code&gt;newProductNumber&lt;/code&gt; and &lt;code&gt;products&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The value of the  Inspector is more clearly seen if you set breakpoints in Javascript functions defined in the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; section. These run every time the function is referenced and the Inspector is great for tracking progress through these.&lt;/p&gt;

&lt;p&gt;Unfortunately, debugging with breakpoints in the "template" section of a &lt;code&gt;+page.svelte&lt;/code&gt; file isn't so straightforward. Here, the code runs every time the page re-renders following a change in a reactive variable, but the code that the "sources" tab displayed here (ie the original Sveltekit code of your &lt;code&gt;+page.svelte&lt;/code&gt;) is not what is now actually running. The original code has been replaced by Svelte scripts generated by the local server. Consequently, there's only limited scope for you to get information about what is happening.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you want to know more about the rendering process and why your &lt;code&gt;+page. svelte&lt;/code&gt; file behaves like this in the Inspector, skip across briefly to the end of &lt;a href="https://dev.to/mjoycemilburn/ngsysv2-44-responsiveadaptive-design-593d"&gt;Post 4.4&lt;/a&gt;. But, be warned, it's complicated!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In my experience, you can usually set breakpoints on Sveltekit statements and thus track the path taken during a re-render. But these won't display field values.&lt;/p&gt;

&lt;p&gt;You can, however, usefully set breakpoint in embedded functions. For example, try setting a breakpoint in the &lt;code&gt;products.push({productNumber: newProductNumber});&lt;/code&gt; line for the "Register" button. Note that the entire block of &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; code gets highlighted (in grey) reminding you that this is an "anonymous function" (get chatGPT to explain this to you sometime). Now rerun the webapp and observe how the breakpoint behaves just as it did in the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; section. Having asked the webapp to register a &lt;code&gt;newProductNumber&lt;/code&gt; of &lt;code&gt;123&lt;/code&gt; and clicked the "Register" button you'll see the webapp halt on the breakpointed line. "Step over next function call" and mouse over the &lt;code&gt;products.push({productNumber: newProductNumber});&lt;/code&gt; line. You'll note that it now shows you that the &lt;code&gt;products&lt;/code&gt; array now contains the single value &lt;code&gt;123&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;If all else fails and you still need to check the value of a variable in the template section, try adding something along the lines of &lt;code&gt;&amp;lt;p&amp;gt;Value of x is {x}&amp;lt;/p&amp;gt;&lt;/code&gt; statement (where, I'm sure you'll understand, "x", is the reactive variable you want to monitor).&lt;/p&gt;

&lt;p&gt;Note that the buttons on the "Paused in debugger" line are duplicated in the Inspector menu bar and are supplemented by a "Step into next function" button to enable you to traverse nested functions. &lt;/p&gt;

&lt;p&gt;As you'll quickly realise, breakpoint settings persist in webapp files. Breakpointed lines are toggled off by clicking them again. A "right-click" in the entry for a file in the Inspector's "Breakpoints" panel on the right of the screen will reveal a master toggle to remove all breakpoints. &lt;/p&gt;

&lt;p&gt;Finally. note that when a webapp hits a major problem and "crashes", opening the Inspector will reveal a red "error count" at the right-hand end of its menu bar. When this happens, details of the problem can be viewed in the Inspector's "console" tab.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>svelte</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>NgSysV2-2.3: Creating a simple Svelte Information System with Google's Firestore</title>
      <dc:creator>MartinJ</dc:creator>
      <pubDate>Tue, 26 Nov 2024 08:05:33 +0000</pubDate>
      <link>https://dev.to/mjoycemilburn/ngsysv2-23-creating-a-simple-svelte-information-system-with-googles-firestore-551b</link>
      <guid>https://dev.to/mjoycemilburn/ngsysv2-23-creating-a-simple-svelte-information-system-with-googles-firestore-551b</guid>
      <description>&lt;p&gt;&lt;em&gt;This post series is indexed at &lt;a href="https://ngatesystems.com" rel="noopener noreferrer"&gt;NgateSystems.com&lt;/a&gt;. You'll find a super-useful keyword search facility there too.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Last reviewed: Feb '25&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Introduction
&lt;/h3&gt;

&lt;p&gt;Most webapps exist purely to create and access shared information. Consider, Amazon's &lt;code&gt;https://www.amazon.co.uk/&lt;/code&gt; website. The essential purpose of this system is to let you browse a central collection of product details, place orders and monitor progress on delivery. To make this work, Amazon must:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maintain this information somewhere accessible over the web&lt;/li&gt;
&lt;li&gt;Structure and manage it to ensure near-instant access and total integrity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This post is all about the "database" technology used to achieve these aims.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Warning&lt;/strong&gt; - this is a &lt;strong&gt;long&lt;/strong&gt; post because database reads and writes in Svelte draw you remorselessly into using SvelteKit's client-server architecture. Previously, your code ran exclusively "client-side" in your web browser. Now you'll also be running code on the local server launched by &lt;code&gt;npm run dev&lt;/code&gt;. This has consequences...&lt;/p&gt;

&lt;p&gt;I've looked at ways of splitting the post up but they don't work. To make matters worse, the Javascript you'll use contains many new features. So, I'm sorry - you're just going to have to suck it up.&lt;/p&gt;

&lt;p&gt;But look on the bright side. Once you're through this, things will start to get easier. Take it slowly. Use chatGPT where you feel I've not explained something clearly. You'll find the bot particularly helpful when you need advice on JavaScript syntax. Relax. This is going to be fun!&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Configuring your project to use Google's Firestore
&lt;/h3&gt;

&lt;p&gt;There are innumerable ways of storing shared data on the web. This post series uses Google's Firestore system because it suits beginners. It requires minimal setup and fits comfortably within the structure of a Svelte webapp.&lt;/p&gt;

&lt;p&gt;You'll need to perform four initial steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Obtain a Google account&lt;/li&gt;
&lt;li&gt;Create a Firebase project under this account&lt;/li&gt;
&lt;li&gt;Register your "webapp"&lt;/li&gt;
&lt;li&gt;Initialise a Firestore Database for your Firebase project&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Firebase&lt;/strong&gt; is Google's umbrella term for many different services you might use to mount a simple project on the web. The services for a given account are managed via Google's "Firebase console" at &lt;code&gt;https://console.firebase.google.com/&lt;/code&gt;. They include both a "Storage" service that enables you to upload files into the Google Cloud and a "Firestore Database" service. A database differs from a file in that it possesses a configurable structure. It enables you to access and update discrete elements of the configured data set.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.1 Obtaining a Google account
&lt;/h4&gt;

&lt;p&gt;If you have a Gmail address, you're already covered because this automatically counts as a Google account. If you don't, follow the instructions at &lt;a href="https://support.google.com/accounts/answer/27441?hl=en" rel="noopener noreferrer"&gt;Create a Google Account&lt;/a&gt; to get one. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you find system development work addictive, you'll quickly find yourself managing multiple projects. You'll then find it useful to create separate accounts for each of these. In IT work, account keys are your most precious possessions. It makes sense to maintain firewalls between projects by giving each project a separate account, each secured by a different password.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  2.2 Creating a Firebase project for your code
&lt;/h4&gt;

&lt;p&gt;Launch the &lt;a href="https://console.firebase.google.com/" rel="noopener noreferrer"&gt;Google Firebase console&lt;/a&gt; and log in with your Google Account (noting that, if you're logged into Gmail with this, you're already logged into the Firebase Console as well). Now click the "Create a Project" box to launch the process.&lt;/p&gt;

&lt;p&gt;Google will want you to supply a name for your project (I suggest you use the project name you're using in VSCode), and will propose an extension that makes this a unique "Project Identifier" within the Firebase world. So for, example, &lt;strong&gt;my&lt;/strong&gt; version of the "Svelte-dev" project used in this post series is known to Google as "Svelte-dev-afbaf".&lt;/p&gt;

&lt;p&gt;&lt;em&gt;As an aside, since the Project Identifier will ultimately form part of the default live URL for your webapp, and since Google lets you edit its initial "uniqueness extension" proposal, you might be tempted to try to change this to something meaningful. However, I suggest you forget this idea. Firstly, you'll find it difficult to pick an identifier that suits both parties. Secondly, in my experience, these "default URLs" aren't ever indexed by Google. A "custom URL" purchased at minimal cost and linked to your default URL when you go live is by far the best way to get a memorable URL.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now click "Continue" to reveal a "Google Analytics" registration page. You can safely ignore this here as it is relevant only to performance issues on live apps. Use the slider bar to decline it and click the "Create Project" button to continue.&lt;/p&gt;

&lt;p&gt;The lights now dim a little as Google registers your project. Finally, once you've clicked one more "Continue" button, you'll find yourself in your project's Firebase Console window. Here's a screenshot of the Firestore tab for a "svelte-dev" project:&lt;/p&gt;

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

&lt;p&gt;It's worth giving yourself a moment to familiarise yourself with this page because it is a little complicated. The basic structure consists of a "tools menu" on the left that determines what gets displayed in the main panel on the right. The problem is that the menu is "adaptive" and maintains a "Project shortcuts" section that remembers where you've been. Consequently, the menu seems to look different every time you open the console! Once you've grasped this feature, however, things work well. Note that the complete set of tools is hidden within the "Build", "Run" and "Analytics" submenus of their parent "Product Categories" menu item. The &lt;strong&gt;"Build"&lt;/strong&gt; set contains everything you need at present.&lt;/p&gt;

&lt;p&gt;Before you proceed further, note the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;information at the top of the screen confirms that the svelte-test project is currently registered on the "Spark" plan. This means that everything you're doing at present is free. Eventually, in this post series, you &lt;strong&gt;will&lt;/strong&gt; get to a point where you need to start to paying Google, and will need to upgrade your project to the "Blaze" tariff. But don't worry - this is a long way off yet, you won't pay much, and you can create a monthy budget to limit the amount Google might try to charge you.&lt;/li&gt;
&lt;li&gt;project details are revealed by clicking the "Project Overview" button at the top of the toolbar. Details available here include a reminder of the Project Identifier and a button to delete the project. If everything goes wrong, you can always use this to rub out the mess and start over again. This won't cost you anything&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2.3 Registering your webapp
&lt;/h4&gt;

&lt;p&gt;Firebase needs to know what your webapp will be called: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click the &lt;code&gt;&amp;lt;/&amp;gt;&lt;/code&gt; icon below the "Get started" message and supply a nickname when requested. I suggest you use your project name again here (eg "svelte-dev"). &lt;/li&gt;
&lt;li&gt;Ignore the offer to "Set up Firebase Hosting for this app" because,  when we finally get around to deployment, all your hosting needs will be handled by App Engine. &lt;/li&gt;
&lt;li&gt;Finally, click "Register" and "Continue to the console" to return to the initial console screen.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2.4 - Initialising a Firestore database
&lt;/h4&gt;

&lt;p&gt;Select "Firestore Database" from the "Build" stack in the tools menu to obtain the Firebase console view shown below:&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%2F5vev9gwi8nm5lwpn2i70.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5vev9gwi8nm5lwpn2i70.jpg" alt="Graphic showing Firebase console database-initialisation screen" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you've clicked the "Create Database" button, the console will want you to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Set your database "Name and Location". "Name" is the identifier for the database and will only be relevant if you plan to create several different databases in your project. Leave this empty, for now, so that Google uses its "default" setting. "Location" specifies where your database will be physically located. The pull-down list of options available here is possibly your first sight of the scale of the Google Cloud service. Its server farms are available right around the globe. You will probably want to select a server close to your location. For example, I generally use "europe-west2 : Heathrow" since I am based in the UK. Pages elsewhere in the Google Cloud console enable you to specify performance and availability characteristics, but you don't need to look at these for now.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Secure your database with "Rules". The screen here offers you a choice between setting initial "production" and "test" "rules". This only makes sense, of course, if you know what "rules" are in the first place - and this isn't the right time for me to explain them. Unless you know better, I'd like you to check the "test mode" option here. Be assured, I'll come back to this later when I talk about "authorisation" (and, oh boy, is there a lot to talk about!).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once you're through these two stages, the Cloud Firestore page opens in the Firebase Console. What now?&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Working with a Firestore Database
&lt;/h3&gt;

&lt;p&gt;This section aims to answer the following questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What is a database? &lt;/li&gt;
&lt;li&gt;What does a Firestore database look like?&lt;/li&gt;
&lt;li&gt;How can I initialise a database in the Firestore console? &lt;/li&gt;
&lt;li&gt;How can I access a Firestore database in Javascript?&lt;/li&gt;
&lt;li&gt;How can I get a Svelte &lt;code&gt;+page.svelte&lt;/code&gt; file to load data from a Firestore database?&lt;/li&gt;
&lt;li&gt;How can I get a Svelte &lt;code&gt;+page.svelte&lt;/code&gt; file to add data to a Firestore database?&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  3.1 What is a database?
&lt;/h4&gt;

&lt;p&gt;For our immediate purposes, a database is a set of tables containing rows of values for named data "fields". So, for example, a Customer Order" database might contain &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a "Customers" table full of "Customer Id" and "Customer Address Details" field values&lt;/li&gt;
&lt;li&gt;a "Products" table full of "Product Number" and "Product Detail" fields&lt;/li&gt;
&lt;li&gt;a "Customer Orders" table with details of the orders for a "Product Number" placed by a "Customer Id"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important thing is that such an arrangement is &lt;strong&gt;structured&lt;/strong&gt; with consistent standards for the naming and formatting of data content&lt;/p&gt;

&lt;h4&gt;
  
  
  3.2 What does a Firestore database look like?
&lt;/h4&gt;

&lt;p&gt;In Firestore, tables are called "collections" and rows within them are called "documents". Documents stored within a collection aren't all required to have the same fields, but field names and content are expected to follow consistent patterns throughout the collection.&lt;/p&gt;

&lt;p&gt;An important feature of a Firestore document is that it should have a unique identifier or "key". Sometimes there will be a field such as "Email Address" within each document that you can use to provide a "natural" unique key. If not, Firestore can be asked to create an artificial key automatically.&lt;/p&gt;

&lt;p&gt;Database design is probably the most challenging part of system development and, once again, I will duck this because the issues involved will only become clear when you've had some practical experience. However, when you have a moment, you will find it useful to check out the &lt;a href="https://firebase.google.com/docs/firestore/data-model" rel="noopener noreferrer"&gt;Cloud Firestore Data model&lt;/a&gt; page.&lt;/p&gt;

&lt;h4&gt;
  
  
  3.3 How can I initialise a database in the Firestore console?
&lt;/h4&gt;

&lt;p&gt;In this post, I plan to show you how to create a single &lt;code&gt;products&lt;/code&gt; collection in your default Firestore database. This will contain simple documents containing a &lt;code&gt;product number&lt;/code&gt; field with a key automatically generated by Firestore.&lt;/p&gt;

&lt;p&gt;On the Firestore page on the Firebase console, click the "Start collection" button and enter the name "products" in the "Collection ID"  field of the popup that now appears.&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%2F3q5mzuisdhatgdr2670u.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3q5mzuisdhatgdr2670u.jpg" alt="Graphic showing Firebase console collection-initialisation screen" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now use the data entry page to create a test products document containing a "productNumber" field with a numeric value of "1" and a "productDetails" field with a text value of "Product 1". &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type "productNumber" in the "Field" entry box to set the field name, set the "Type" box to "number" and enter "1" (without the quote characters) in the "Value" box.
&lt;/li&gt;
&lt;li&gt;Click "add field and type "productDetails" in the "Field" entry box to set the field name, leave the "Type" box set to its default "string" setting and enter "Product 1" (without the quote characters) in the "Value" box. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now sign off the document by first clicking the "Auto Id" button and then "Saving" it Here's what the console should look like now:&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%2Fmeg2nzbf6kduafv0dq7w.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmeg2nzbf6kduafv0dq7w.jpg" alt="Graphic showing Firebase console document-display screen" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you wanted to add further documents, you would click "add document" at this point, but that's not necessary in this case - you only need a single document to demonstrate your webapp's ability to read it. &lt;/p&gt;

&lt;p&gt;You're finished here for now, but note that the console's "panel view"  lets you edit or delete the document you've just created. If you've got in a complete mess you can even delete the entire collection and start again. &lt;/p&gt;

&lt;h4&gt;
  
  
  3.4 How can I access a Firestore database in Javascript?
&lt;/h4&gt;

&lt;p&gt;Here's where things start to get &lt;strong&gt;really&lt;/strong&gt; interesting! &lt;/p&gt;

&lt;p&gt;Google provides a library of Javascript functions to let you read and write Firestore documents. Such libraries are referred to as "APIs" (Application Program Interfaces). Have a look at the following code that shows how the &lt;code&gt;firebase/firestore&lt;/code&gt; library would be used to read all the documents in &lt;code&gt;svelte-dev&lt;/code&gt;'s &lt;code&gt;products&lt;/code&gt; collection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getDocs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orderBy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase/firestore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initializeApp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase/app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getFirestore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase/firestore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firebaseConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AIzaSyCE933 ... klfhFdwQg1IF1pWaR1iE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;authDomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;svelte-dev-afbaf.firebaseapp.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;svelte-dev-afbaf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;storageBucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;svelte-devt-afbaf.appspot.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;messagingSenderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1027 ... 85697&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1:1027546585697:web:27002bf ..... b0f088e820&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firebaseApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;initializeApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firebaseConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getFirestore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firebaseApp&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;productsCollRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productsQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productsCollRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;productNumber&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;asc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productsSnapshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getDocs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productsQuery&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;currentProducts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="nx"&gt;productsSnapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;currentProducts&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="na"&gt;productNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;productNumber&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentProducts&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// accessed in +page.svelte as data.products&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Concentrate on the section that starts &lt;code&gt;const productsCollRef = collection(db, "products");&lt;/code&gt;. This uses Firestore API calls to load a sorted copy of all documents within the &lt;code&gt;products&lt;/code&gt; collection into the State &lt;code&gt;currentProducts&lt;/code&gt; variable. &lt;/p&gt;

&lt;p&gt;First, the &lt;code&gt;collection&lt;/code&gt; and &lt;code&gt;query&lt;/code&gt; functions, drawn from the Firestore Client API library, are used to point Firebase at the &lt;code&gt;products&lt;/code&gt; collection and define a query to run on it. Then the query is launched by a &lt;code&gt;getDocs&lt;/code&gt; API call.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I won't attempt to describe the mechanics of this sequence of Firestore API calls. Treat these as a piece of "boiler-plate code" - code  - the sort of thing that you write once and, ever afterwards, simply copy. Since you'll find you need a whole library of templates to cover the full array of Firestore "read", "update" and "delete" operations, you might find it useful to look at "Firestore CRUD command templates" in &lt;a href="https://dev.to/mjoycemilburn/23-a-beginners-guide-firebase-v9-a-quick-summary-of-firestore-crud-commands-35h6"&gt;Post 10.1&lt;/a&gt;. If you'd like to check out Google's own description of the API, you'll find links to these at the end of Post 10.1.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"CRUD" here is short for "create", "read", "update" and "delete".&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;getDocs&lt;/code&gt; result is returned as an array of documents conventionally called a "snapshot". However, note that the getDocs function call is preceded by an &lt;code&gt;await&lt;/code&gt; keyword. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;await&lt;/code&gt; keyword is needed here because, by default in Javascript, instructions referencing external data sources that may take an unpredictable time to complete are handled &lt;strong&gt;asynchronously&lt;/strong&gt;. The "await" keyword essentially (though this is a gross simplification) enables you to override this arrangement. When you have more time, you might find it useful to have a look at &lt;a href="https://dev.to/mjoycemilburn/a-plain-man-s-guide-to-the-javascript-fetch-api-and-the-await-keyword-49gi"&gt;Post 5.1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But right now, returning to our code snippet above, look at the section starting with the &lt;code&gt;const firebaseConfig&lt;/code&gt; statement. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;firebaseConfig&lt;/code&gt; declaration initialises an object containing the configuration details needed to connect your web app to your specific Firebase project. It includes various keys and identifiers that Firebase uses to locate and authenticate your app. You'll find the settings for &lt;strong&gt;your&lt;/strong&gt; particular webapp in "Project Overview/Project settings" on the Firebase console. The &lt;code&gt;firebaseConfig&lt;/code&gt; settings in the code samples below have been "obfuscated" because they are unique to &lt;strong&gt;my&lt;/strong&gt; project and aren't to be passed around lightly (more about this shortly). When trying the sample code below, you'll need to copy in the &lt;code&gt;firebaseConfig&lt;/code&gt; settings from your own project.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;firebaseConfig&lt;/code&gt; initialised, the webapp can initialise the &lt;code&gt;db&lt;/code&gt; variable required by the query's &lt;code&gt;const productsCollRef = collection(db, "products");&lt;/code&gt; statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firebaseApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;initializeApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firebaseConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getFirestore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firebaseApp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, you may be wondering where these API functions come from. The answer is that they are &lt;code&gt;imported&lt;/code&gt; from locations in your project by the three statements at the top of the code block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getDocs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orderBy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase/firestore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initializeApp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase/app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getFirestore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase/firestore&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;Here "modular libraries" are being accessed to supply functions for your code. Named functions such as &lt;code&gt;collection&lt;/code&gt; are extracted from a parent module to fulfil the references required later in the code.&lt;/p&gt;

&lt;p&gt;But then this leads to the question "And how do modular libraries get into my project in the first place?" The answer, of course, is that you have to put them there, and the tool you use to do this is faithful old &lt;code&gt;npm&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Back in a VSCode svelte-test terminal session (terminate the dev server if necessary with a couple of ctrl-C keystrokes) and run the following instruction'&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="nx"&gt;firebase&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a minute or two (the installation involves a sizeable download), you'll be poised to run code that downloads a Firestore database collection. But, you still don't know how to embed this in a Svelte webapp. So, on to the next question...&lt;/p&gt;

&lt;h4&gt;
  
  
  3.5 How can I get a Svelte &lt;code&gt;+page.svelte&lt;/code&gt; file to load data from a Firestore database?
&lt;/h4&gt;

&lt;p&gt;This has been a long haul but, hang in there, you're nearly finished. &lt;/p&gt;

&lt;p&gt;Currently, in the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; section of your &lt;code&gt;src/routes/+page.svelte&lt;/code&gt; file, you have the following statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you know, this declares your &lt;code&gt;products&lt;/code&gt; field as a State variable and initialises it as an empty array. What you want to do now is replace "empty" with the content of the &lt;code&gt;products&lt;/code&gt; Firestore collection.&lt;/p&gt;

&lt;p&gt;Unfortunately, as you've seen, this involves an asynchronous operation. This complicates things somewhat because Svelte doesn't want anything to slow down the initial load of a page - it's happy to see information added later but, the first user impression should be of an instantaneous response. Svelte has a standard arrangement for loading initial data into a &lt;code&gt;+page.svelte&lt;/code&gt; file. It goes like this:&lt;/p&gt;

&lt;p&gt;First, you create a new &lt;code&gt;src/routes/+page.server.js&lt;/code&gt; file that wraps all your asynchronous code inside a &lt;code&gt;load()&lt;/code&gt; function (mandatory name) and returns its results as an object. &lt;/p&gt;

&lt;p&gt;Here's the code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/routes/+page.server.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getDocs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orderBy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase/firestore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initializeApp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getApps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getApp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;firebase/app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getFirestore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase/firestore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;load&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;firebaseConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// replace with settings from your own project!!&lt;/span&gt;
    &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AIzaSyCE933v....vklfhFdwQg1IF1pWaR1iE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// obfuscated code&lt;/span&gt;
    &lt;span class="na"&gt;authDomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;svelte-test-afbaf.firebaseapp.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;svelte-test-afbaf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;storageBucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;svelte-test-afbaf.appspot.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;messagingSenderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1027 ..c585697&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1:1027546585697:web:27002bf  ...ccccb0f088e820&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// obfuscated code&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// see Post 3.3 for an explanation of the "ternary" expression used below&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firebaseApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;getApps&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;initializeApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firebaseConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getApp&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;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getFirestore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firebaseApp&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;productsCollRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productsQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productsCollRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;productNumber&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;asc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productsSnapshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getDocs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productsQuery&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;currentProducts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="nx"&gt;productsSnapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;currentProducts&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="na"&gt;productNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;productNumber&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentProducts&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;load&lt;/code&gt; function above returns an object with a single &lt;code&gt;products&lt;/code&gt; property whose value is the &lt;code&gt;currentProducts&lt;/code&gt; array constructed by the Firestore API calls.&lt;/p&gt;

&lt;p&gt;This is all very well, but how is this to be conveyed to the &lt;code&gt;products&lt;/code&gt; state variable in &lt;code&gt;+page.svelte&lt;/code&gt;? &lt;/p&gt;

&lt;p&gt;The first step is to advertise a new &lt;code&gt;data&lt;/code&gt; (mandatory name) state variable as a &lt;strong&gt;prop&lt;/strong&gt; (short for "property") of &lt;code&gt;+page.svelte&lt;/code&gt;You do this by declaring it with an &lt;code&gt;export&lt;/code&gt; keyword, thus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Props won't be covered in this series until you get to &lt;a href="https://dev.to/mjoycemilburn/ngatesystems-post-series-v2-24-4421"&gt;Post 3.1&lt;/a&gt; and learn about "components". For now, think of your &lt;code&gt;+page.svelte&lt;/code&gt; file as a function with &lt;code&gt;data&lt;/code&gt; as its parameter.&lt;/p&gt;

&lt;p&gt;When you run your &lt;code&gt;+page.svelte&lt;/code&gt; file now, the SvelteKit framework sees the &lt;code&gt;export let data&lt;/code&gt; declaration with its reserved &lt;code&gt;data&lt;/code&gt; keyword and thinks, "ah, I need to run the load() function associated with this page". The &lt;code&gt;products&lt;/code&gt; data is duly returned into the &lt;code&gt;data&lt;/code&gt; prop of &lt;code&gt;+page.svelte&lt;/code&gt; and now, since this is a reactive variable, the page is refreshed.&lt;/p&gt;

&lt;p&gt;All you need to do to make your existing "template" code work with the new arrangement is to replace &lt;code&gt;products&lt;/code&gt; references with &lt;code&gt;data.products&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The &lt;code&gt;+page.server.js&lt;/code&gt; file is your first sight of "server-side" code in Svelte - that is, code that runs in the server. All the &lt;code&gt;+page.svelte&lt;/code&gt; code you've seen so far runs "client-side" in the browser. A &lt;code&gt;+page.server.js&lt;/code&gt; file, by contrast, runs either in the local server launched by &lt;code&gt;npm run dev&lt;/code&gt; or, after deployment, in the Node.js environment of an App Engine server. Server-side code runs faster than client-side code and is secure. The only person who can view or change it is you - its owner.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here's the complete code for a modified version of the &lt;code&gt;+page.svelte&lt;/code&gt; file from &lt;a href="https://dev.to/mjoycemilburn/ngsysv2-22-creating-a-simple-reactive-svelte-webapp-39jc"&gt;Post 2.2&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/routes/+page.svelte - remove before running&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;popupVisible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;newProductNumber&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;let&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// An array of data.product objects returned by +page.svelte.js&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-align: center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Products&lt;/span&gt; &lt;span class="nx"&gt;Maintenance&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;popupVisible&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!--&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;popup&lt;/span&gt; &lt;span class="nx"&gt;not&lt;/span&gt; &lt;span class="nx"&gt;visible&lt;/span&gt;&lt;span class="o"&gt;--&amp;gt;&lt;/span&gt;

        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Currently&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;registered&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="nx"&gt;Numbers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!--&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;  &lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="o"&gt;--&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;margin: 0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;productNumber&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sr"&gt;/each&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;
            &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;margin-top: 1rem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;   &lt;span class="c1"&gt;// a function to toggle the popup on&lt;/span&gt;
                &lt;span class="nx"&gt;popupVisible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Add&lt;/span&gt; &lt;span class="nx"&gt;Another&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
    &lt;span class="p"&gt;{:&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!--&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="nx"&gt;registration&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="o"&gt;--&amp;gt;&lt;/span&gt;

        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;
            &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;border:1px solid black; height: 6rem; width: 30rem; margin: auto;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="nx"&gt;Registration&lt;/span&gt; &lt;span class="nx"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;newProductNumber&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/label&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;
                &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// a function to add the product number to the array and hide the popup&lt;/span&gt;
                    &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;products&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="na"&gt;productNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newProductNumber&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
                    &lt;span class="nx"&gt;popupVisible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="nx"&gt;newProductNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Reset the input field&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;Register&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/form&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sr"&gt;/if&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you now replace the contents of your current &lt;code&gt;src/routes/+page.svelte&lt;/code&gt; file with the above code, update the&lt;code&gt;firebaseConfig&lt;/code&gt; settings in &lt;code&gt;+page.server.js&lt;/code&gt; with parameters from your &lt;strong&gt;own&lt;/strong&gt; project), and start your dev server as before, the page should initialise with the single "Product Number 1" record you created in the &lt;code&gt;products&lt;/code&gt; collection earlier. Adding new Products should also work exactly as before.&lt;/p&gt;

&lt;p&gt;But there's a snag. When you restart the webapp you'll see that you're back to square one with a single "Product Number 1" record. Can you see the problem? Yes, the webapp is still updating the in-core list of &lt;code&gt;products&lt;/code&gt; (now located in the new &lt;code&gt;data.products&lt;/code&gt; variable). You need to find some way of replacing this with code that updates the &lt;code&gt;products&lt;/code&gt; database. Onward and upward!&lt;/p&gt;

&lt;h4&gt;
  
  
  3.6 How can I get a Svelte &lt;code&gt;+page.svelte&lt;/code&gt; file to add data to a  Firestore database?
&lt;/h4&gt;

&lt;p&gt;What you need is another piece of Firestore API boilerplate code  - this time a sequence that creates a new &lt;code&gt;{productNumber: newProductNumber}&lt;/code&gt; document in a Firestore &lt;code&gt;products&lt;/code&gt; collection (to simplify things, I'm ditching the &lt;code&gt;productDetails&lt;/code&gt; property for now). Here it is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productsDocData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;productNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newProductNumber&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;productsCollRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productsDocRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productsCollRef&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productsDocRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;productsDocData&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where should this code be located? Currently, the code fired by the "Add Another Product" &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;'s &lt;code&gt;on:click&lt;/code&gt; button lives in your &lt;code&gt;+page.svelte&lt;/code&gt; file. But Svelte advises that, for security and efficiency reasons, the database update should be performed "server-side" in &lt;code&gt;+page.server.js&lt;/code&gt; in an &lt;code&gt;actions()&lt;/code&gt; function paralleling the &lt;code&gt;load()&lt;/code&gt; function you've already created here. The function is triggered by "posting" data from the &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Here's what the new &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; arrangement in &lt;code&gt;+page.svelte&lt;/code&gt; should look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/routes/+page.svelte - fragment&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;popupVisible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;
    &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;border:1px solid black; height: 6rem; width: 30rem; margin: auto;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Products&lt;/span&gt; &lt;span class="nx"&gt;Registration&lt;/span&gt; &lt;span class="nx"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;newProductNumber&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/label&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Register&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/form&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most important thing here is the presence of the &lt;code&gt;method=POST&lt;/code&gt; qualifier on the &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; specification. When the form is submitted, this makes SvelteKit think "ah, I now need to pass the data collected by this form to &lt;code&gt;actions()&lt;/code&gt; in a &lt;code&gt;+page.server.js&lt;/code&gt; file associated with this page". Because no explicit "action" is requested above, SvelteKit goes looking for a "default" &lt;code&gt;actions()&lt;/code&gt; function. Here is is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/routes/+page.server.js - fragment&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;actions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// retrieve the newProductNumber string from the form and convert it to a number&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productNumberAsText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;formData&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;newProductNumber&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newProductNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productNumberAsText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productsDocData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;productNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newProductNumber&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;productsCollRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productsDocRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productsCollRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productsDocRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;productsDocData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;updateSucceeded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="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 object returned by the "actions" function becomes available to the &lt;code&gt;+page.svelte&lt;/code&gt; file through the &lt;code&gt;form&lt;/code&gt; prop declared in its &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; section. Here, an &lt;code&gt;export let form;&lt;/code&gt; statement parallels the earlier use of a &lt;code&gt;data&lt;/code&gt; prop to return the results of a &lt;code&gt;load()&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;Here are the complete versions of &lt;code&gt;+page.svelte&lt;/code&gt; and &lt;code&gt;+page.server.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// routes/+page.svelte - remove before running&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;popupVisible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="c1"&gt;//The "data" State variable will be initialised by +page.server.js as an array of {productNumber : value}&lt;/span&gt;
&lt;span class="c1"&gt;// objects&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-align: center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Products&lt;/span&gt; &lt;span class="nx"&gt;Maintenance&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;popupVisible&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!--&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;popup&lt;/span&gt; &lt;span class="nx"&gt;not&lt;/span&gt; &lt;span class="nx"&gt;visible&lt;/span&gt;&lt;span class="o"&gt;--&amp;gt;&lt;/span&gt;

    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Currently&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;registered&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!--&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="o"&gt;--&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;margin: 0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;productNumber&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sr"&gt;/each&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;
        &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;display: inline; margin-top: 1rem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;popupVisible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Add&lt;/span&gt; &lt;span class="nx"&gt;Another&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;{:&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!--&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="nx"&gt;registration&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="o"&gt;--&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;  &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;border:1px solid black; height: 6rem; width: 30rem; margin: auto;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="nx"&gt;Registration&lt;/span&gt; &lt;span class="nx"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; 
            &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;newProductNumber&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/label&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Register&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/form&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sr"&gt;/if&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// routes/+page.server.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getDocs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setDoc&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase/firestore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initializeApp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase/app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getFirestore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase/firestore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firebaseConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AIzaSyDOVyss .... LsT6n-tWh0GZoPQhM&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;authDomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;svelte-dev-80286.firebaseapp.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;svelte-dev-80286&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;storageBucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;svelte-dev-80286.appspot.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;messagingSenderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;5855 ...  06025&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1:585552006025:web:e41 .....  fcc161e6f58&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firebaseApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;initializeApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firebaseConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getFirestore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firebaseApp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;load&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;productsCollRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productsQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productsCollRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;productNumber&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;asc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="c1"&gt;// Because the productNumber field in the products collection is numeric rather than string, orderBy&lt;/span&gt;
  &lt;span class="c1"&gt;// produces numeric sorted order.  If the field were a string, the order would be "lexicographic" which&lt;/span&gt;
  &lt;span class="c1"&gt;// means "12" sorts before "3". It's a nuisance, however, since later we will be inputting productNumber as&lt;/span&gt;
  &lt;span class="c1"&gt;// a string in order to demonstrate validation t in productNumberIsNumeric and also using it&lt;/span&gt;
  &lt;span class="c1"&gt;// to demonstrate dynamic routes, which also demand the use of strings. The .toString and parseInt&lt;/span&gt;
  &lt;span class="c1"&gt;// javascript functions are used to perform conversions where required.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productsSnapshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getDocs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productsQuery&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;currentProducts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="nx"&gt;productsSnapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;currentProducts&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="na"&gt;productNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;productNumber&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentProducts&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// retrieve the newProductNumber string from the form and convert it to a number&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productNumberAsText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;formData&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;newProductNumber&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newProductNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productNumberAsText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productsDocData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;productNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newProductNumber&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;productsCollRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productsDocRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productsCollRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productsDocRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;productsDocData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;updateSucceeded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you copy this code, remember to reset the firestoreConfig data again. If you have problems getting it to work, remember also the advice in the previous post about debugging client-side code and look at the "Postscript sections" below for advice about fixing server-side problems. Good luck!&lt;/p&gt;

&lt;p&gt;Note that the FireStore API imports and &lt;code&gt;db&lt;/code&gt; configuration statements in &lt;code&gt;+page.server.js&lt;/code&gt; have been given "file scope" by moving them out of the functions they serve and relocating them at the top of the file's &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; section. In a more complex project, it would be common practice to configure &lt;code&gt;db&lt;/code&gt; in a separate &lt;code&gt;lib&lt;/code&gt; folder to enable it to be shared more widely as an &lt;code&gt;import&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You'll also note that the new code drops the resetting of the &lt;code&gt;popupVisible&lt;/code&gt; field that previously appeared in the old &lt;code&gt;+page.svelte&lt;/code&gt; file's &lt;code&gt;on:click&lt;/code&gt; function. Svelte's default action on form submission is to reload the page. Consequently, when the form has been processed, &lt;code&gt;popupVisible&lt;/code&gt; is re-initialised as false, the "Currently-registered products:" array is refreshed from the updated Firestore &lt;code&gt;products&lt;/code&gt; collection and the popup disappears. As a bonus, the new Product Number will be inserted in the correct sorted order, courtesy of the &lt;code&gt;orderBy("productNumber", "asc")&lt;/code&gt; qualifier in the &lt;code&gt;products&lt;/code&gt; &lt;code&gt;getDocs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For cases where your page contains multiple forms, you'll want to be able to specify multiple &lt;code&gt;actions()&lt;/code&gt; functions. See &lt;a href="https://svelte.dev/docs/kit/form-actions#Named-actions" rel="noopener noreferrer"&gt;Named Actions&lt;/a&gt; in Svelte Docs for how to do this.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Caveat: code like this will only work while your Firestore database is public. When you add rules to restrict a collection's access to authorised users (ie users who have "logged in"), the Firestore templates you've been using here will fail. Posts in the next section of this series will explain why and &lt;a href="https://dev.to/mjoycemilburn/ngsysv2-34-a-serious-svelte-infosys-a-rules-friendly-version-5g2c"&gt;Post 3.4&lt;/a&gt; in particular will explain how you can fall back to code based purely on client-side &lt;code&gt;+page.svelte&lt;/code&gt; files. However, since this will be at the expense of security and performance, I hope you'll fight your way through these "minor irritations" and continue developing client-server code. For the present, make sure that your database rules look something like:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="o"&gt;=**&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Allow both read and write access&lt;/span&gt;
      &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="nx"&gt;read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Summary
&lt;/h3&gt;

&lt;p&gt;I imagine that this post will have strained your patience to the limit. If you've reached this point with a working webapp and your sanity still intact, give yourself a gold star - you've covered most of the core Svelte topics and got a handle on the essential skills! &lt;/p&gt;

&lt;p&gt;That said, there's still a lot to learn. For example, this post has ducked out of describing basic error handling, and form validation arrangements. Beyond that, I also want to introduce you to Svelte routes(ie pages), layouts (page headers and trailers), components, and the tricky issues surrounding security (login design and database rules). Finally, there's the interesting question of how you deploy your webapp for live operation on the web. I hope you'll read on!&lt;/p&gt;

&lt;h3&gt;
  
  
  Postcript: When things go wrong
&lt;/h3&gt;

&lt;p&gt;The introduction of "server-side" processing in your webapp has fast-tracked you straight into the senior developer league. This is because the "Chrome Inspector" techniques that I hope you've enjoyed using on your &lt;code&gt;+page.svelte&lt;/code&gt; files don't work on &lt;code&gt;+page.server.js&lt;/code&gt; files. But all is not lost. Here's an introduction to the techniques that a senior developer would use:&lt;/p&gt;

&lt;h4&gt;
  
  
  Postscript (a): Debugging Server side code in the Terminal Session
&lt;/h4&gt;

&lt;p&gt;Although the VSCode editor will do its best to help you produce sound code, some basic errors will only become apparent when the SvelteKit server tries to run your webapp. At this point, your screen may display a "500 - Internal Error" message (if it displays anything at all!). The browser can't help you much here because a &lt;code&gt;+page.server.js&lt;/code&gt; file is essentially invisible here. While the Source tab's Page/localhost storage hierarchy continues to show your &lt;code&gt;+page.svelte&lt;/code&gt; file, it has nothing to say about &lt;code&gt;+page.server.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But the Inspector knows that an error has occurred and can sometimes give you useful a clue as to its cause. The right-hand end of the menu bar will show a red icon with a cross in the middle. Click the console tab and see what it has to say, but don't expect full details of the problem. wf you need &lt;strong&gt;full&lt;/strong&gt; details, you'll find these in the terminal session where you launched the server with &lt;code&gt;npm run dev&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The problem here is that you'll likely feel the level of detail is probably rather &lt;strong&gt;too&lt;/strong&gt; full. Every error is reported with a "call stack" that details the full sequence of server function calls that preceded the failure point. Here's a short one precipitated by an incorrect field name declaration (I deliberately mistyped &lt;code&gt;const db=&lt;/code&gt; as &lt;code&gt;const dba =&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;ReferenceError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;not&lt;/span&gt; &lt;span class="nx"&gt;defined&lt;/span&gt;
    &lt;span class="nx"&gt;at&lt;/span&gt; &lt;span class="nf"&gt;load &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;Users&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;mjoyc&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;Desktop&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;GitProjects&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;svelte&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;at&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_server_data &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;Users&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;mjoyc&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;Desktop&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;GitProjects&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;svelte&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;node_modules&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;sveltejs&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;kit&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;load_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;61&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;at&lt;/span&gt; &lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;Users&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;mjoyc&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;Desktop&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;GitProjects&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;svelte&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;node_modules&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;sveltejs&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;kit&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;140&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;   
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A terminal window isn't a good place to view information like this. Sometimes you have to scroll up or down to find what you want, and information may be additionally obscured by output from other activities. Nevertheless, it's all you've got, so you need to make the best of it. Personally, when dealing with something particularly tricky, I have found it often helps to "clear the ground" by killing the terminal session, restarting with &lt;code&gt;npm run dev&lt;/code&gt; and re-running the problem code. This gives you a clean statement of any error messages and ensures the problem isn't just down to internal server issues. &lt;/p&gt;

&lt;p&gt;However, in the example shown above, the cause of the error is signalled very clearly - the server has encountered a reference to a &lt;code&gt;db&lt;/code&gt; variable in &lt;code&gt;+page.server.js&lt;/code&gt; at line 18 column 38 and &lt;code&gt;db&lt;/code&gt; hasn't been declared. In this case, the terminal session has given you everything you need to know, I think.&lt;/p&gt;

&lt;p&gt;The terminal window can help you fix problems with your logic, too. In the past, "debugging" was usually carried out by placing &lt;code&gt;console.log&lt;/code&gt; messages at strategic points in the code, and it's easy enough to use this approach here. A typical log message would be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;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="s2"&gt;Arrived at X with a= &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A log message like this placed in a &lt;code&gt;+page.server.js&lt;/code&gt; file will be displayed in the VSCode terminal window (by contrast, log statements in a client-side &lt;code&gt;+page.svelte&lt;/code&gt; code will continue to be displayed in the &lt;strong&gt;browser's&lt;/strong&gt; console window).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The arguments to the  console.log() statement can be configured in many different ways. These are designed variously to assist with the labelling of the statement (i.e. tying a message to its source) and the formatting of its content. You might find it helpful to ask ChatGPT for a tutorial on the subject. A variant I find really useful takes the form "console.log({x})", where "x" can be any variable, regardless of variable type. When "x" is an object, for example, this will produce terminal session output of the form "{x: { ... object content ...}}"&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Postscript (b): Debugging Server-side code in the  VSCode debugger
&lt;/h4&gt;

&lt;p&gt;Console.log messages are fine for fixing minor issues, but by now you expect to be able to use something with the sophistication of the browser "breakpoint" debugging tool. Fear not. You can now find this in the VSCode. editor. Here's what you do: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the &lt;code&gt;+page.server.js&lt;/code&gt; file you want to debug and set a breakpoint at a point you'd like to inspect. You do this by mousing over the source line you want to use as a breakpoint and clicking on the pale red dot that will appear at its left-hand side. The pale red dot now changes to bright red.&lt;/li&gt;
&lt;li&gt;In the VSCode command palette (shortcut "ctrl shift p") choose &lt;code&gt;Debug: Attach to node process&lt;/code&gt; and get a list of projects in your workspace. Select the one you want to debug.&lt;/li&gt;
&lt;li&gt;Note that the terminal session that you've just launched is labelled as "Javascript Debug Terminal". Type 'npm run dev` into this terminal and note how the usual Vite output that appears includes an additional "Debugger attached" line. Note also how VSCode's activity bar has turned orange&lt;/li&gt;
&lt;li&gt;Now mouse over the &lt;code&gt;http://localhost&lt;/code&gt; address displayed by Vite and launch the webapp with a "ctrl-click". Your webapp now opens in a popup window.&lt;/li&gt;
&lt;li&gt;You'll now find that the editor page for your &lt;code&gt;+page.server.js&lt;/code&gt; file has become an active debugging session with execution halted on the first breakpoint. A panel at the top of the editor window displays the familiar "advance to next breakpoint" etc buttons, and mousing over field names will reveal tooltips that display their values. As before, if a debug session is halted on a variable assignment statement, you'll only see the result of this when you advance to the next statement.
&lt;/li&gt;
&lt;li&gt;Terminate the debug session by mousing over the icon at the far end of the debug controls panel. This toggles between "disconnect" and  "stop" actions when you press the "alt" key. Click this when it displays "disconnect" and the orange VSCode activity bar will turn blue again. If you want to resume debugging, "ctrl click" on the webapp URL in the debugging terminal window again (which will stay active until you explicitly "bin" it)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The screenshot below shows the &lt;code&gt;+page.server.js&lt;/code&gt; file for this post halted on the &lt;code&gt;return&lt;/code&gt; from the &lt;code&gt;load()&lt;/code&gt; function. A "mouseover" on the &lt;code&gt;products&lt;/code&gt; property for the return at this point displays the result of reading the Firestore &lt;code&gt;products&lt;/code&gt; collection.&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%2F6zz5naobz843u8pnxepg.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6zz5naobz843u8pnxepg.jpg" alt="Graphic illustrating server-side debugging in VSCode" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For full details of the facilities available in the VSCode debugger, see the documentation at &lt;a href="https://code.visualstudio.com/docs/editor/debugging" rel="noopener noreferrer"&gt;VSCode debugging&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>svelte</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>NgSysV2-3.4: A Serious Svelte InfoSys: Rules-friendly version</title>
      <dc:creator>MartinJ</dc:creator>
      <pubDate>Mon, 25 Nov 2024 21:45:28 +0000</pubDate>
      <link>https://dev.to/mjoycemilburn/ngsysv2-34-a-serious-svelte-infosys-a-rules-friendly-version-5g2c</link>
      <guid>https://dev.to/mjoycemilburn/ngsysv2-34-a-serious-svelte-infosys-a-rules-friendly-version-5g2c</guid>
      <description>&lt;p&gt;&lt;em&gt;This post series is indexed at &lt;a href="https://ngatesystems.com" rel="noopener noreferrer"&gt;NgateSystems.com&lt;/a&gt;. You'll find a super-useful keyword search facility there too.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Last reviewed: Nov '24&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Introduction
&lt;/h3&gt;

&lt;p&gt;This series has previously described how you can use the Svelte framework in conjunction with Google's Firestore database Client API to develop useful information systems quickly and enjoyably. Sadly, however, &lt;a href="https://dev.to/mjoycemilburn/ngsysv2-33-a-serious-svelte-infosys-firebase-db-rules-and-login-2g70"&gt;Post 3.3&lt;/a&gt; revealed how Firebase's otherwise excellent authentication system doesn't support Firestore activity in server-side &lt;code&gt;load()&lt;/code&gt; and &lt;code&gt;actions()&lt;/code&gt; functions where database rules reference the &lt;code&gt;auth&lt;/code&gt; object. &lt;/p&gt;

&lt;p&gt;Skipping your Firestore authorisation rules isn't an option - without these, your database is wide open to anyone who can hijack the &lt;code&gt;firebaseConfig&lt;/code&gt; keys from your webapp. This post describes ways to re-work the Svelte server-side code so that it runs on the client side while Firestore rules remain firmly in place.  &lt;/p&gt;

&lt;h3&gt;
  
  
  2. Reworking 'compromised' &lt;code&gt;load()&lt;/code&gt; functions
&lt;/h3&gt;

&lt;p&gt;Not all &lt;code&gt;load()&lt;/code&gt; functions will be affected by the presence of Firestore rules. Those that reference Firestore &lt;strong&gt;public&lt;/strong&gt; collections will still run happily server-side. The Client API is still available in &lt;code&gt;+page.server.js&lt;/code&gt; files - it just won't work if it's asked to use collections protected by &lt;code&gt;auth&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;If your &lt;code&gt;load()&lt;/code&gt; function addresses public files and you simply want to avoid server-side debugging, you might consider moving your &lt;code&gt;load()&lt;/code&gt; function into a &lt;code&gt;+page.js&lt;/code&gt; file. This works exactly like a &lt;code&gt;+page.server.js&lt;/code&gt; file - Svelte will still run the function automatically at load time. But this happens client-side now where it can be debugged in the browser. See Svelte docs at &lt;a href="https://svelte.dev/docs/kit/load" rel="noopener noreferrer"&gt;Loading data&lt;/a&gt; for details &lt;/p&gt;

&lt;p&gt;However, a 'compromised' &lt;code&gt;load()&lt;/code&gt; function (typically where Firestore rules are used to ensure that users can only access their own data) must be relocated into client-side code. Typically, this would be reworked as a new, appropriately named, function in the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; section of its associated &lt;code&gt;+page.svelte&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;But now you must find a way to launch your relocated &lt;code&gt;load()&lt;/code&gt; function automatically on page initialisation - you no longer benefit from Svelte's built-in arrangements for native &lt;code&gt;load()&lt;/code&gt; functions. The problem is that your re-located function is asynchronous and so can't be launched directly from a &lt;code&gt;+page.svelte&lt;/code&gt; file's &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; section. This problem is solved by using Svelte's &lt;code&gt;onMount&lt;/code&gt; utility.&lt;/p&gt;

&lt;p&gt;"OnMount" is a Svelte lifecycle "hook" that runs automatically when a webapp page is launched. Inside an &lt;code&gt;onMount()&lt;/code&gt; you can now safely &lt;code&gt;await&lt;/code&gt; your relocated &lt;code&gt;load()&lt;/code&gt; function - you may recall that you met it earlier in the &lt;code&gt;logout&lt;/code&gt; function. You can find a description at &lt;a href="https://svelte.dev/docs/svelte/lifecycle-hooks#onMount" rel="noopener noreferrer"&gt;Svelte Lifecycle Hooks&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Reworking 'compromised' &lt;code&gt;actions()&lt;/code&gt; functions
&lt;/h3&gt;

&lt;p&gt;In this case, there are no options. Compromised &lt;code&gt;actions()&lt;/code&gt; functions must be relocated into the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; section of the parent &lt;code&gt;+page.svelte&lt;/code&gt; file. Form submit buttons here must be reworked to "fire" the action via &lt;code&gt;on:click&lt;/code&gt; arrangements referencing the relocated function.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Example: Rules-friendly versions of the &lt;code&gt;products-display&lt;/code&gt; page
&lt;/h3&gt;

&lt;p&gt;In the following code examples, a new &lt;code&gt;products-display-rf&lt;/code&gt; route displays the old "Magical Products" list of &lt;code&gt;productNumbers&lt;/code&gt;. The &lt;code&gt;load()&lt;/code&gt; used here isn't compromised but its code is still moved to a &lt;code&gt;+page.js&lt;/code&gt; file to let you confirm that you can debug it in the browser. The only other changes are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the code now includes the extensions to display the &lt;code&gt;productDetails&lt;/code&gt; field when a product entry in the "Magical Products" list is clicked.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;firebase-config&lt;/code&gt; is now imported from the &lt;code&gt;lib&lt;/code&gt; file introduced in the last post
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/routes/products-display-rf/+page.svelte&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-align: center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Current&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="nx"&gt;Numbers&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h3&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!--&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;separate&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="o"&gt;--&amp;gt;&lt;/span&gt;
         &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;  &lt;span class="nx"&gt;style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;margin-top: .35rem;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/products-display-rf/{product.productNumber}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;View&lt;/span&gt; &lt;span class="nx"&gt;Detail&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;productNumber&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;a
&lt;/span&gt;        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sr"&gt;/each&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/routes/products-display-rf/+layout.svelte&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;display:flex; justify-content:space-between&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/about&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;About&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/a&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Magical&lt;/span&gt; &lt;span class="nx"&gt;Products&lt;/span&gt; &lt;span class="nx"&gt;Company&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/inventory_search&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Search&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/a&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/header&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;slot&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/slot&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;trailer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-align: center; margin: 3rem; font-weight: bold; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;©&lt;/span&gt; &lt;span class="mi"&gt;2024&lt;/span&gt; &lt;span class="nx"&gt;Magical&lt;/span&gt; &lt;span class="nx"&gt;Products&lt;/span&gt; &lt;span class="nx"&gt;Company&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/trailer&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// routes/products-display-rf/+page.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getDocs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orderBy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase/firestore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$lib/utilities/firebase-client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;load&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;productsCollRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productsQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productsCollRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;productNumber&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;asc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productsSnapshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getDocs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productsQuery&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;currentProducts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="nx"&gt;productsSnapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;currentProducts&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="na"&gt;productNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;productNumber&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentProducts&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/routes/products-display-rf/[productNumber]/+page.svelte&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;goto&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$app/navigation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-align: center;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Here&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;s the Product Details for Product {data.productNumber} : {data.productDetails}&amp;lt;/span
  &amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div style="text-align: center;"&amp;gt;
  &amp;lt;button
    style="display: inline-block; margin-top: 1rem"
    on:click={() =&amp;gt; {
      goto("/products-display-rf");
    }}
  &amp;gt;
    Return&amp;lt;/button
  &amp;gt;
&amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/routes/products-display-rf/[productNumber]/+page.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getDocs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;where&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase/firestore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$lib/utilities/firebase-client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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;productNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;productNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Now that we have the product number, we can fetch the product details from the database&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productsCollRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productsQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productsCollRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;productNumber&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="nx"&gt;productNumber&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;productsSnapshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getDocs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productsQuery&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;productDetails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;productsSnapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;productDetails&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;productNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;productNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;productDetails&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;productDetails&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;Copy this code into new files in "-rf" suffixed folders. But do take care as you're doing this - working with lots of confusing &lt;code&gt;+page&lt;/code&gt; files in VSCode's cramped folder hierarchies requires close concentration. When you're done, run your dev server and test the new page at the &lt;code&gt;http://localhost:5173/products-display-rf&lt;/code&gt; address. &lt;/p&gt;

&lt;p&gt;The "Products Display" page should look exactly the same as before but, when you click through,  the "Product Details" page should now display dynamically-generated content.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Example: Rules-friendly version of the &lt;code&gt;products-maintenance&lt;/code&gt; page
&lt;/h3&gt;

&lt;p&gt;Things are rather more interesting in a client-side version of the &lt;code&gt;products-maintenance&lt;/code&gt; page. &lt;/p&gt;

&lt;p&gt;Because your Firebase rule for the &lt;code&gt;products&lt;/code&gt; collection now reference &lt;code&gt;auth&lt;/code&gt; (and thus requires prospective users to be "logged-in"), the &lt;code&gt;actions()&lt;/code&gt; function that adds a new product document is compromised. So this has to be moved out of its &lt;code&gt;+page.server.js&lt;/code&gt; file and relocated into the parent &lt;code&gt;+page.svelte&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Here, the function is renamed as &lt;code&gt;handleSubmit()&lt;/code&gt; and is "fired" by an &lt;code&gt;on:submit={handleSubmit}&lt;/code&gt; clause on the &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; that collects the product data. &lt;/p&gt;

&lt;p&gt;Although the &lt;code&gt;products-maintenance&lt;/code&gt; page doesn't have a &lt;code&gt;load()&lt;/code&gt; function, &lt;code&gt;onMount&lt;/code&gt; still features in the updated &lt;code&gt;+pagesvelte&lt;/code&gt; file. This is because &lt;code&gt;onMount&lt;/code&gt; provides a useful way of usefully redirecting users who try to run the maintenance page before they've logged-in. &lt;/p&gt;

&lt;p&gt;See if you can follow the logic listed below in a new &lt;code&gt;products-maintenance-rf/+page.svelte&lt;/code&gt; file and an updated &lt;code&gt;/login/+page.svelte&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/routes/products-maintenance-rf/+page.svelte&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;onMount&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;svelte&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setDoc&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase/firestore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$lib/utilities/firebase-client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;goto&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$app/navigation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$lib/utilities/firebase-client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;productNumberIsNumeric&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$lib/utilities/productNumberIsNumeric&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;productNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;productDetails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;formSubmitted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;databaseUpdateSuccess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;databaseError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;productNumberClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;productNumber&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;submitButtonClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submitButton&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;onMount&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/login?redirect=/products-maintenance-rf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Prevent default form submission behavior&lt;/span&gt;
        &lt;span class="nx"&gt;formSubmitted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Reset formSubmitted at the beginning of each submission&lt;/span&gt;

        &lt;span class="c1"&gt;// Convert productNumber to an integer&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newProductNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productsDocData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;productNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newProductNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;productDetails&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;productDetails&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="k"&gt;try&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;productsCollRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productsDocRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productsCollRef&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productsDocRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;productsDocData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="nx"&gt;databaseUpdateSuccess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;formSubmitted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Set formSubmitted only after successful operation&lt;/span&gt;

            &lt;span class="c1"&gt;// Clear form fields after successful submission&lt;/span&gt;
            &lt;span class="nx"&gt;productNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;productDetails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;databaseUpdateSuccess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;databaseError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;formSubmitted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Ensure formSubmitted is set after error&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
            &lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;productNumber&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;productNumber&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;productNumberClass&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nl"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;formSubmitted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;productNumberIsNumeric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productNumber&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;submitButtonClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submitButton validForm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="nx"&gt;productNumberClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;productNumber&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;submitButtonClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submitButton error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="nx"&gt;productNumberClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;productNumber error&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="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/label&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;nbsp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;nbsp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="nx"&gt;Details&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
            &lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;productDetails&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;productDetails&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;productNumberClass&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/label&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;nbsp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;nbsp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;productNumberClass&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;productNumber error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Invalid&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;Please&lt;/span&gt; &lt;span class="nx"&gt;enter&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sr"&gt;/if&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;submitButtonClass&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Submit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/form&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;formSubmitted&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;databaseUpdateSuccess&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Form&lt;/span&gt; &lt;span class="nx"&gt;submitted&lt;/span&gt; &lt;span class="nx"&gt;successfully&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;{:&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Form&lt;/span&gt; &lt;span class="nx"&gt;submission&lt;/span&gt; &lt;span class="nx"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;databaseError&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sr"&gt;/if&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sr"&gt;/if&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;style&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;productNumber&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt; &lt;span class="nx"&gt;solid&lt;/span&gt; &lt;span class="nx"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="nx"&gt;rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;submitButton&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inline&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;margin&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validForm&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;palegreen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/style&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/routes/login/+page.svelte&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;onMount&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;svelte&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;goto&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$app/navigation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// SvelteKit's navigation for redirection&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$lib/utilities/firebase-client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;signInWithEmailAndPassword&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase/auth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;redirect&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;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;onMount&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;// Parse the redirectTo parameter from the current URL&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;urlParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;redirect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;urlParams&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;redirect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loginWithMail&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;signInWithEmailAndPassword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;login with Mail failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;login-form&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Login&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loginWithMail&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Login&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/form&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;style&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;login&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;flex&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;align&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;justify&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="nx"&gt;vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="nx"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt; &lt;span class="nx"&gt;solid&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;ccc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;border&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;background&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;f5f5f5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt; &lt;span class="nf"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&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;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;margin&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt; &lt;span class="nx"&gt;solid&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;ccc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;border&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;background&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;007&lt;/span&gt;&lt;span class="nx"&gt;bff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;border&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;background&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;4285&lt;/span&gt;&lt;span class="nx"&gt;f4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;border&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/style&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test this by starting your dev server and launching the &lt;code&gt;/products-maintenance-rf&lt;/code&gt; page. Because you're not logged you'll be redirected immediately to the &lt;code&gt;login&lt;/code&gt; page. Note that the URL displayed here includes the &lt;code&gt;products-maintenance-rf&lt;/code&gt; return address as a parameter. &lt;/p&gt;

&lt;p&gt;Once you're logged in, the &lt;code&gt;login&lt;/code&gt; page should send you back to &lt;code&gt;products-maintenance-rf&lt;/code&gt;. Since you're now logged in, the new version of the product input form (which now includes a product detail field) will be displayed.&lt;/p&gt;

&lt;p&gt;The input form works very much as before, but note how it is cleared after a successful submission, enabling you to enter further products. Use your &lt;code&gt;products-display-rf&lt;/code&gt; page to confirm that new products and associated product details data are being added correctly.&lt;/p&gt;

&lt;p&gt;Note also, that when a user is logged in, you can use the &lt;code&gt;auth&lt;/code&gt; object thus created to obtain user details such as email address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userEmail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check with chatGPT to find out what else is available from &lt;code&gt;auth&lt;/code&gt;. For example, you might use the user's &lt;code&gt;uid&lt;/code&gt; to display only documents 'owned' by that user. &lt;/p&gt;

&lt;h3&gt;
  
  
  6. Summary
&lt;/h3&gt;

&lt;p&gt;If you have simple objectives, the "Rules-friendly" approach described here, for all its deficiencies, may be perfectly adequate for your needs. Client-side Svelte provides a wonderful playground to develop personal applications quickly and painlessly. But be aware of what you've lost:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;efficient data loading - there is likely to be a delay before data appears on the screen. &lt;/li&gt;
&lt;li&gt;secure input validation &lt;/li&gt;
&lt;li&gt;assured SEO &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, if you have your sights set on a career as a serious software developer, please read on. But be warned, things get rather more "interesting"! &lt;/p&gt;

</description>
      <category>sveltekit</category>
      <category>firebase</category>
      <category>firestore</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
