<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: François</title>
    <description>The latest articles on Forem by François (@faz).</description>
    <link>https://forem.com/faz</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%2F1244295%2Fbead71ca-a4bd-4e4b-9b9a-054b8f17df5d.jpg</url>
      <title>Forem: François</title>
      <link>https://forem.com/faz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/faz"/>
    <language>en</language>
    <item>
      <title>Modern Web App - Start of the Journey</title>
      <dc:creator>François</dc:creator>
      <pubDate>Tue, 09 Jan 2024 19:52:10 +0000</pubDate>
      <link>https://forem.com/faz/modern-web-app-start-of-the-journey-3j4n</link>
      <guid>https://forem.com/faz/modern-web-app-start-of-the-journey-3j4n</guid>
      <description>&lt;p&gt;Recently, I wanted to test a few technologies and use this excuse to start a new project around &lt;a href="https://paizo.com/starfinder/"&gt;StarFinder&lt;/a&gt; as a support for my discovery journey 😄&lt;/p&gt;

&lt;p&gt;This post and the next ones, are the documentation of this journey and, as such, are not a "tutorial" on starting a new project with nextjs, vitest or typescript but the sum of my findings and choices. See them as my "travel log"!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Project
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://paizo.com/starfinder/"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mdlucPTz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paizo.com/image/content/Starfinder/logo.png" alt="StarFinder" width="600" height="102"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My goal is to implement a wizard to help in the character creation process for the tabletop role playing game &lt;a href="https://paizo.com/starfinder/"&gt;StarFinder&lt;/a&gt;. A few solutions already exists but I found them lacking on the UI/UX part and I wanted to have a solution that guide the user while having a more modern look.&lt;/p&gt;

&lt;p&gt;My github repo: &lt;a href="https://github.com/superfaz/starfinder"&gt;https://github.com/superfaz/starfinder&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;First goal: Creation&lt;/strong&gt; - A user should be able to create a level 1 character based on the official rule book extended with the character option book. As a result, he should have a proper character sheet (at least printed; potentially online) that he can use to play his character.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Second goal: Level-up&lt;/strong&gt; - A user should be able to apply its level-ups and be guided in doing so (automatic updates and help provided to know what is authorized to him or not).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Third goal: Sharing&lt;/strong&gt; - A user should be able to share its character sheets with other people and in particular Game Masters.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My (initial) Stack
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://skillicons.dev"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oJCxSHlQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://skillicons.dev/icons%3Fi%3Dnextjs%2Cts%2Cazure" alt="My Stack" width="161" height="48"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I wanted to do this project with the following key technologies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;typescript&lt;/strong&gt; with &lt;strong&gt;swc&lt;/strong&gt; for coding&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;nextjs&lt;/strong&gt; to test its Server-Side Rendering capabilities&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;copilot&lt;/strong&gt; in support as the project will be open-source&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;azure static web app&lt;/strong&gt; for deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;As the project moved forward this stack has evolved and I will give more details about those changes on later posts.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The First Step
&lt;/h2&gt;

&lt;p&gt;My initial objective was to simply set-up the project, create the associated github space and prepare the release via github actions and azure static web app. And start to code my proof-of-concept.&lt;/p&gt;

&lt;p&gt;During this process, I did a few interesting discoveries regarding node support on github and azure stacks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Github Actions Runners is using Node 18 per default (and not the 20 which is the latest LTS at the moment).&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2204-Readme.md"&gt;https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2204-Readme.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The following step can be added to a github action workflow to install the desired version:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Node.js environment&lt;/span&gt;
    &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
    &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;20.x"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SWA deployment, based on Oryx, is using node 16 per default. That's indeed a strange choice. Oryx will read the &lt;code&gt;package.json&lt;/code&gt; file of the project to determine the version of node to use based on the &lt;code&gt;engine&lt;/code&gt;/&lt;code&gt;node&lt;/code&gt; property:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"engines"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;18.0.0"&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;/li&gt;
&lt;li&gt;
&lt;p&gt;SWA runner doesn't support node 20, even if the deployment allows it. You have to add a restriction on the same &lt;code&gt;engines&lt;/code&gt;/&lt;code&gt;node&lt;/code&gt; property:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"engines"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;18.0.0 &amp;lt;20.0.0"&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;p&gt;Sources: &lt;a href="https://github.com/Azure/static-web-apps/issues/1349"&gt;https://github.com/Azure/static-web-apps/issues/1349&lt;/a&gt; and &lt;a href="https://github.com/Azure/static-web-apps-cli/issues/756"&gt;https://github.com/Azure/static-web-apps-cli/issues/756&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Optimizing the workflow with Cache
&lt;/h2&gt;

&lt;p&gt;In order to keep the Github Actions workflow running smoothly, adding some cache for &lt;strong&gt;yarn&lt;/strong&gt; and &lt;strong&gt;nextjs&lt;/strong&gt; was a mandatory step.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;nextjs&lt;/strong&gt;, the configuration is straightforward and based on the official documentation:&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://nextjs.org/docs/pages/building-your-application/deploying/ci-build-caching"&gt;https://nextjs.org/docs/pages/building-your-application/deploying/ci-build-caching&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache for Next&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.workspace }}/.next/cache&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}&lt;/span&gt;
          &lt;span class="na"&gt;restore-keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For yarn, the official documentation extracts the path of the global cache before using &lt;code&gt;actions/cache&lt;/code&gt;. I preferred to move the cache to a known location to simplify the configuration. Here is my  set-up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Yarn&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;corepack enable&lt;/span&gt;
          &lt;span class="s"&gt;yarn config set enableGlobalCache false&lt;/span&gt;
          &lt;span class="s"&gt;yarn config set cacheFolder .yarn/cache&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache for Yarn&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.workspace }}/.yarn/cache&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}&lt;/span&gt;
          &lt;span class="na"&gt;restore-keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;${{ runner.os }}-yarn-&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Proof of Concept
&lt;/h2&gt;

&lt;p&gt;While developing the first elements of the app with nextjs, it appears quite quickly that I will not exploit the Server-Side Rendering a lot at the beginning. As using Client rendering is required for a lot of user interactions.&lt;/p&gt;

&lt;p&gt;A small configuration was required for the nextjs build to successfully being deployed as a SWA. In the next.config.mjs, I added the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GITHUB_WORKFLOW&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Configuration for Azure Static Web Apps&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;Azure Static Web Apps configuration&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="nx"&gt;standardConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;standalone&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="c1"&gt;// Default configuration&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;standardConfig&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 POC was able to help the user to select a Race, a Theme and a Class while providing meaningful data about those choices (and sub-forms when needed).&lt;/p&gt;

&lt;p&gt;This implementation was based on json files for the data. And it was quite clear for me that one of the next steps will be to extract those into a NoSQL data source.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--O2tKEApo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tz47ehztz4j3mk0f6kzo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--O2tKEApo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tz47ehztz4j3mk0f6kzo.png" alt="Screenshot of the project" width="800" height="395"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;current status&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Next step: Adding tests
&lt;/h2&gt;

&lt;p&gt;The next post will be about the deployment of &lt;strong&gt;vitest&lt;/strong&gt; for the project and why it was finally required to do a switch to &lt;strong&gt;jest&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>programming</category>
      <category>azure</category>
    </item>
  </channel>
</rss>
