<?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: Peter Mekhaeil</title>
    <description>The latest articles on Forem by Peter Mekhaeil (@petermekhaeil).</description>
    <link>https://forem.com/petermekhaeil</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%2F517972%2F0ad2e7c6-9f26-4796-80e0-56e52a0f808b.png</url>
      <title>Forem: Peter Mekhaeil</title>
      <link>https://forem.com/petermekhaeil</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/petermekhaeil"/>
    <language>en</language>
    <item>
      <title>Accelerate your delivery with modern tools</title>
      <dc:creator>Peter Mekhaeil</dc:creator>
      <pubDate>Tue, 27 Dec 2022 14:23:28 +0000</pubDate>
      <link>https://forem.com/petermekhaeil/accelerate-your-delivery-with-modern-tools-1eg0</link>
      <guid>https://forem.com/petermekhaeil/accelerate-your-delivery-with-modern-tools-1eg0</guid>
      <description>&lt;p&gt;By using modern tools in the development of large front-end repositories, we can potentially improve the speed of deliverables. This will benefit both the business and the development team. The business will benefit from a faster time to market, and the development team will be more motivated as a faster pipeline reduces the need for context switching. I'll share 3 development tools that will lead to faster delivery of your development.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use a fast package manager
&lt;/h3&gt;

&lt;p&gt;A fast package manager will improve developer productivity by reducing the time it takes to install packages and manage dependencies. This will lead to reducing build times and faster feature delivery.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pnpm.io/"&gt;pnpm&lt;/a&gt; is described as a fast, disk space-efficient package manager. It focuses on &lt;a href="https://pnpm.io/motivation"&gt;saving disk space while boosting installation speed&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;pnpm is performant because it uses a &lt;a href="https://pnpm.io/symlinked-node-modules-structure"&gt;content-addressable store&lt;/a&gt; that contains each version of each dependency and pnpm will hard link the project's &lt;code&gt;node_modules&lt;/code&gt; to that store. This means that pnpm only stores one copy of each package in the store, regardless of how many projects are using it. This strategy will save disk space and improve the speed of installing and managing packages.&lt;/p&gt;

&lt;p&gt;I've seen this result in projects speeding up their installations by &lt;strong&gt;75%&lt;/strong&gt;. The team behind pnpm have also posted &lt;a href="https://pnpm.io/benchmarks"&gt;benchmarks&lt;/a&gt; on how pnpm compares to other package managers in terms of installation speed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Migrate to a fast development tool
&lt;/h3&gt;

&lt;p&gt;A fast development tool can reduce the time it takes to build and deploy. This can be particularly important for large applications that is frequently deployed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://vitejs.dev/"&gt;Vite&lt;/a&gt; is a modern JavaScript development tool that focuses on providing a fast and efficient development experience. It uses a new approach to building and serving web applications using native browser capabilities to load modules on demand, providing a faster and more efficient development experience.&lt;/p&gt;

&lt;p&gt;One of the advantages Vite provides is how incredibly fast it is at building. Vite uses rollup for bundling and &lt;a href="https://esbuild.github.io/"&gt;esbuild&lt;/a&gt; for transforming. Esbuild is written in Go and is 10-100x faster than the JavaScript-based alternatives.&lt;/p&gt;

&lt;p&gt;Vite includes &lt;a href="https://vitejs.dev/guide/build.html#library-mode"&gt;Library Mode&lt;/a&gt; that allows you to bundle libraries that do not have an HTML entry. Not only can you use Vite to build your applications, you can also take advantage of the power of Vite for packages that are consumed by those applications.&lt;/p&gt;

&lt;p&gt;Swapping out a create-react-app (CRA) app to Vite is a matter of replacing the npm scripts and reconfiguring &lt;code&gt;vite.config.js&lt;/code&gt; to match your needs. I've seen real world CRA apps migrate to Vite and have improved their build times by &lt;strong&gt;50%&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use a build system
&lt;/h3&gt;

&lt;p&gt;Build systems like &lt;a href="https://turbo.build/repo"&gt;Turborepo&lt;/a&gt; and &lt;a href="https://nx.dev/"&gt;Nx&lt;/a&gt; can cache the results of tasks to speed up the build process in subsequent builds.&lt;/p&gt;

&lt;p&gt;Each task in a CI pipeline has inputs and outputs - for example, a build task may have source code as inputs and bundled files as outputs. When a task is run, the build system generates a hash based on the inputs and stores the output under that hash. If the task is run again and the inputs have not changed, the build system can reuse the saved output stored under the same hash, rather than running the task again.&lt;/p&gt;

&lt;p&gt;This can be especially helpful for large projects with many tasks, as it reduces the overhead of running tasks that have not changed, and can significantly reduce CI time.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>pnpm</category>
      <category>vite</category>
      <category>turborepo</category>
    </item>
    <item>
      <title>How to build an npx starter template</title>
      <dc:creator>Peter Mekhaeil</dc:creator>
      <pubDate>Mon, 05 Sep 2022 10:58:18 +0000</pubDate>
      <link>https://forem.com/petermekhaeil/how-to-build-an-npx-starter-template-2ibh</link>
      <guid>https://forem.com/petermekhaeil/how-to-build-an-npx-starter-template-2ibh</guid>
      <description>&lt;p&gt;Our favourite frameworks have starter templates to help us get started with setting up our projects with minimal configuration. Some known examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://reactjs.org/docs/create-a-new-react-app.html#create-react-app"&gt;Create React App&lt;/a&gt; (&lt;code&gt;npx create-react-app my-app&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nextjs.org/docs/api-reference/create-next-app"&gt;Next.js&lt;/a&gt; (&lt;code&gt;npx create-next-app@latest&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://remix.run/docs/en/v1#getting-started"&gt;Remix&lt;/a&gt; (&lt;code&gt;npx create-remix@latest&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I have created a &lt;a href="https://github.com/petermekhaeil/create-my-template"&gt;sample starter template&lt;/a&gt; that I will walk through in this article.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How they work&lt;/strong&gt; : these npm packages have an executable file configured in the &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 javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&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;create-my-template&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;version&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;1.0.0&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;bin&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;create-my-template&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;./index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the user installs the npm package, it will also install the executable configured in &lt;code&gt;bin&lt;/code&gt;, allowing the user to run the executable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;create-my-template&lt;span class="nv"&gt;$ &lt;/span&gt;create-my-template
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;npx&lt;/code&gt; is part of &lt;code&gt;npm&lt;/code&gt;: it allows you to run a command from an npm package. If the package has a single &lt;code&gt;bin&lt;/code&gt; script configured, that command will be used. &lt;code&gt;npx&lt;/code&gt; will also fetch the package remotely if it is not installed locally.&lt;/p&gt;

&lt;p&gt;This means users run the below command and &lt;code&gt;npx&lt;/code&gt; will download the package and run the executable in one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npx create-my-template
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we can execute a command, let's write up an installation script that will generate a starter template. A basic script can look like the one below:&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="cp"&gt;#!/usr/bin/env node
&lt;/span&gt;
&lt;span class="c1"&gt;// Usage: npx create-my-template my-app&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;spawn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cross-spawn&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;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&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;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// The first argument will be the project name.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;projectName&lt;/span&gt; &lt;span class="o"&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;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// Create a project directory with the project name.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentDir&lt;/span&gt; &lt;span class="o"&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;cwd&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;projectDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;projectName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mkdirSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;projectDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;recursive&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="c1"&gt;// A common approach to building a starter template is to&lt;/span&gt;
&lt;span class="c1"&gt;// create a `template` folder which will house the template&lt;/span&gt;
&lt;span class="c1"&gt;// and the files we want to create.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;templateDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;template&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cpSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;templateDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;projectDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;recursive&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="c1"&gt;// It is good practice to have dotfiles stored in the&lt;/span&gt;
&lt;span class="c1"&gt;// template without the dot (so they do not get picked&lt;/span&gt;
&lt;span class="c1"&gt;// up by the starter template repository). We can rename&lt;/span&gt;
&lt;span class="c1"&gt;// the dotfiles after we have copied them over to the&lt;/span&gt;
&lt;span class="c1"&gt;// new project directory.&lt;/span&gt;
&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;renameSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;projectDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gitignore&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;projectDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.gitignore&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;projectPackageJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;projectDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;package.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Update the project's package.json with the new project name&lt;/span&gt;
&lt;span class="nx"&gt;projectPackageJson&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="nx"&gt;projectName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;projectDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;package.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;projectPackageJson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Run `npm install` in the project directory to install&lt;/span&gt;
&lt;span class="c1"&gt;// the dependencies. We are using a third-party library&lt;/span&gt;
&lt;span class="c1"&gt;// called `cross-spawn` for cross-platform support.&lt;/span&gt;
&lt;span class="c1"&gt;// (Node has issues spawning child processes in Windows).&lt;/span&gt;
&lt;span class="nx"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;npm&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;install&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;stdio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inherit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Success! Your new project is ready.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Created &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;projectName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; at &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;projectDir&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a basic example. Other things you will need to consider when you build your own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can use tools like &lt;a href="https://www.npmjs.com/package/commander"&gt;commander&lt;/a&gt; or &lt;a href="https://www.npmjs.com/package/inquirer"&gt;inquirer&lt;/a&gt; to add interactive command-line interface.&lt;/li&gt;
&lt;li&gt;Add some error-handling. Example: Check if project directory already exists before attempting to create it.&lt;/li&gt;
&lt;li&gt;Check which package manager the user prefers to use (eg pnpm, yarn).&lt;/li&gt;
&lt;li&gt;Add some styling using &lt;a href="https://www.npmjs.com/package/chalk"&gt;chalk&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Do you want to initalise a git repository in the project directory?&lt;/li&gt;
&lt;li&gt;Should the installation script do a clean up if installation failed?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you're ready and happy with your starter template, you can then &lt;a href="https://docs.npmjs.com/cli/v6/commands/npm-publish"&gt;npm publish&lt;/a&gt; for everyone to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source code examples
&lt;/h2&gt;

&lt;p&gt;A great way to learn is by reading the source code of other starter templates that are known across the community:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/withastro/astro/tree/main/packages/create-astro"&gt;Astro&lt;/a&gt; (&lt;code&gt;npm create astro@latest&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/facebook/create-react-app/tree/main/packages/create-react-app"&gt;Create React App&lt;/a&gt; (&lt;code&gt;npx create-react-app my-app&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/vercel/next.js/tree/canary/packages/create-next-app"&gt;Next.js&lt;/a&gt; (&lt;code&gt;npx create-next-app@latest&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/nuxt/create-nuxt-app"&gt;Nuxt.js&lt;/a&gt; (&lt;code&gt;npx create-nuxt-app my-app&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/sveltejs/kit/tree/master/packages/create-svelte"&gt;SvelteKit&lt;/a&gt; (&lt;code&gt;npm create svelte@latest my-app&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/remix-run/remix/tree/main/packages/create-remix"&gt;Remix&lt;/a&gt; (&lt;code&gt;npx create-remix@latest&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/vercel/turborepo/tree/main/packages/create-turbo"&gt;Turborepo&lt;/a&gt; (&lt;code&gt;npx create-turbo@latest&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/vuejs/create-vue#create-vue"&gt;Vue&lt;/a&gt; (&lt;code&gt;npm init vue@3&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>npm</category>
    </item>
    <item>
      <title>How to keep undefined values in JSON.stringify</title>
      <dc:creator>Peter Mekhaeil</dc:creator>
      <pubDate>Mon, 22 Aug 2022 15:44:53 +0000</pubDate>
      <link>https://forem.com/petermekhaeil/how-to-keep-undefined-values-in-jsonstringify-3kip</link>
      <guid>https://forem.com/petermekhaeil/how-to-keep-undefined-values-in-jsonstringify-3kip</guid>
      <description>&lt;p&gt;The &lt;a href="https://www.json.org/json-en.html"&gt;JSON spec&lt;/a&gt; does not allow &lt;code&gt;undefined&lt;/code&gt; values, so when you try to stringify an object that contains an undefined value, the key will get removed:&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;person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Peter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// '{"name":"Peter"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are workarounds to keep &lt;code&gt;undefined&lt;/code&gt; in the return value, but because it is not part of spec, you will not be able to parse the string back to a JSON. Instead, we can replace it with &lt;code&gt;null&lt;/code&gt; which is valid.&lt;/p&gt;

&lt;p&gt;JSON.stringify has an optional second parameter &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#the_replacer_parameter"&gt;replacer&lt;/a&gt; that can recursively transform properties during the stringify process.&lt;/p&gt;

&lt;p&gt;Using the &lt;code&gt;replacer&lt;/code&gt; parameter of JSON.stringify, we can replace &lt;code&gt;undefined&lt;/code&gt; values with a value that is accepted - such as &lt;code&gt;null&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Peter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;replacer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;replacer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// '{"name":"Peter","age":null}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An example use case where this can come useful is network request payloads in the case we need to let the server know which values are not defined. POST requests have a string body, if we need to send over an object, we require to &lt;code&gt;JSON.stringify&lt;/code&gt; the request. The above approach will help us send over values that are not defined (provided that the server understands &lt;code&gt;null&lt;/code&gt; as an acceptable value).&lt;/p&gt;

</description>
      <category>javascript</category>
    </item>
    <item>
      <title>Proxying Ackee through Netlify</title>
      <dc:creator>Peter Mekhaeil</dc:creator>
      <pubDate>Wed, 29 Jun 2022 02:12:41 +0000</pubDate>
      <link>https://forem.com/petermekhaeil/proxying-ackee-through-netlify-23j</link>
      <guid>https://forem.com/petermekhaeil/proxying-ackee-through-netlify-23j</guid>
      <description>&lt;p&gt;Proxying the requests to &lt;a href="https://ackee.electerious.com/"&gt;Ackee&lt;/a&gt; through Netlify comes with some advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It bypasses the need to configure CORS headers&lt;/strong&gt; - Because Ackee is self-hosted on a domain that is different to your website, during the installation you will be asked to configure CORS headers for Ackee to accept requests from other domains. With proxy rewrites, the browser will be making a request to Ackee through the same domain so we can skip configuring CORS headers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It makes it difficult for ad-blockers to detect the tracker&lt;/strong&gt; - We can rewrite the path to the tracker to not include keywords that can be blocked.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://docs.netlify.com/routing/redirects/"&gt;Netlify Redirects&lt;/a&gt; can redirect requests to external services and this is how we will be setting up our proxy rewrites.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding the rewrite rules
&lt;/h2&gt;

&lt;p&gt;Netlify Redirects rules are configured in a file called &lt;code&gt;_redirects&lt;/code&gt;. Create the file if you do not have one already. Here are the rules to use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/script.js https://your-ackee-domain.com/tracker.js 200
/api https://your-ackee-domain.com/api 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we have done is proxying these paths to an external path on another domain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;https://yourdomain.com/script.js -&amp;gt; https://your-ackee-domain.com/tracker.js
https://yourdomain.com/api -&amp;gt; https://your-ackee-domain.com/api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Update the path in your script tag
&lt;/h2&gt;

&lt;p&gt;You will need to update the Ackee script to reference the new path:&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;script
  &lt;/span&gt;&lt;span class="na"&gt;async&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/script.js"&lt;/span&gt;
  &lt;span class="na"&gt;data-ackee-server=&lt;/span&gt;&lt;span class="s"&gt;"https://yourdomain.com"&lt;/span&gt;
  &lt;span class="na"&gt;data-ackee-domain-id=&lt;/span&gt;&lt;span class="s"&gt;"0000-0000-0000-0000-0000"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verify the changes
&lt;/h2&gt;

&lt;p&gt;Verify the proxy is working by seeing your website making requests to &lt;code&gt;/script.js&lt;/code&gt; and &lt;code&gt;/api&lt;/code&gt; and that they respond with status 200.&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>netlify</category>
    </item>
    <item>
      <title>3 benefits interviews bring you</title>
      <dc:creator>Peter Mekhaeil</dc:creator>
      <pubDate>Tue, 14 Jun 2022 04:09:58 +0000</pubDate>
      <link>https://forem.com/petermekhaeil/3-benefits-interviews-bring-you-312p</link>
      <guid>https://forem.com/petermekhaeil/3-benefits-interviews-bring-you-312p</guid>
      <description>&lt;p&gt;Not only do interviews open doors to new opportunities, they also help you sharpen your skills with new learnings. Interviews keep your career moving forward and I will share some learnings from experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn about what you know
&lt;/h2&gt;

&lt;p&gt;When you answer questions during the interview, be aware to what level you understand the topics being discussed. Speaking out loud about what you know helps you articulate those past career learnings and experiences. With each interview, the more you can improve your confidence in talking about what you know.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn about what you do not know yet
&lt;/h2&gt;

&lt;p&gt;You may not have an answer to some questions during the interview and that is totally fine - this will be an opportunity to learn something new! Ask the interviewer for the answers to those questions. Now whether or not you decide to continue with the interview rounds, you have now learnt something new that you can take to your existing job or you take to your next interview. With each interview, you are building up your knowledge base.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn to negotiate
&lt;/h2&gt;

&lt;p&gt;When you get to the offer stage of the interviews, you have the opportunity to practice negotiation, a very handy skill to have at any level in your career. With each interview, sharpen your negotiation skills and pick up experience in handling those situations.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A great book on this topic is &lt;a href="https://learninpublic.org/"&gt;The Coding Career Handbook&lt;/a&gt; - a chapter is dedicated to salary negotiation where you’ll learn that you have an upper hand simply because the best alternative is staying at the job you already have.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Each experience will be unique
&lt;/h2&gt;

&lt;p&gt;Each person will have their own learning experience. With each interview, practice a useful skill for your career, such as building your confidence, expanding your knowledge and salary negotiating.&lt;/p&gt;

</description>
      <category>career</category>
    </item>
    <item>
      <title>Hex color notation have an alpha channel</title>
      <dc:creator>Peter Mekhaeil</dc:creator>
      <pubDate>Wed, 01 Jun 2022 15:49:38 +0000</pubDate>
      <link>https://forem.com/petermekhaeil/hex-color-notation-have-an-alpha-channel-50o1</link>
      <guid>https://forem.com/petermekhaeil/hex-color-notation-have-an-alpha-channel-50o1</guid>
      <description>&lt;p&gt;The hex color notation can be described as &lt;code&gt;#RGB[A]&lt;/code&gt; or &lt;code&gt;#RRGGBB[AA]&lt;/code&gt; - it accepts an alpha channel that can be used to represent the transparency.&lt;/p&gt;

&lt;p&gt;When using it as &lt;code&gt;#RRGGBB[AA]&lt;/code&gt;, the alpha channel is a hexadecimal number where &lt;code&gt;00&lt;/code&gt; is full transparent and &lt;code&gt;FF&lt;/code&gt; full opaque. If using the shorter &lt;code&gt;#RGB[A]&lt;/code&gt; notation, it is a hexadecimal number ranging from &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;F&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nf"&gt;#FF7f00&lt;/span&gt;     &lt;span class="c"&gt;/* orange               */&lt;/span&gt;
&lt;span class="nf"&gt;#FF7f0000&lt;/span&gt;   &lt;span class="c"&gt;/* orange    0% opaque  */&lt;/span&gt;
&lt;span class="nf"&gt;#FF7f0080&lt;/span&gt;   &lt;span class="c"&gt;/* orange   50% opaque  */&lt;/span&gt;
&lt;span class="nf"&gt;#FF7f00FF&lt;/span&gt;   &lt;span class="c"&gt;/* orange  100% opaque  */&lt;/span&gt;

&lt;span class="err"&gt;#01&lt;/span&gt;&lt;span class="nt"&gt;E&lt;/span&gt;        &lt;span class="c"&gt;/* blue                 */&lt;/span&gt;
&lt;span class="err"&gt;#01&lt;/span&gt;&lt;span class="nt"&gt;E0&lt;/span&gt;       &lt;span class="c"&gt;/* blue       0% opaque */&lt;/span&gt;
&lt;span class="err"&gt;#01&lt;/span&gt;&lt;span class="nt"&gt;E8&lt;/span&gt;       &lt;span class="c"&gt;/* blue      53% opaque */&lt;/span&gt;
&lt;span class="err"&gt;#01&lt;/span&gt;&lt;span class="nt"&gt;EF&lt;/span&gt;       &lt;span class="c"&gt;/* blue     100% opaque */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find out more on &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/hex-color"&gt;MDN&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Today I Learned&lt;/strong&gt;&lt;br&gt;
I share what I learn on &lt;a href="https://petermekhaeil.com/today-i-learned/"&gt;Today I Learned&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>css</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>Logging variables in Chrome DevTools using logpoints</title>
      <dc:creator>Peter Mekhaeil</dc:creator>
      <pubDate>Tue, 24 May 2022 13:49:49 +0000</pubDate>
      <link>https://forem.com/petermekhaeil/logging-variables-in-chrome-devtools-using-logpoints-5fae</link>
      <guid>https://forem.com/petermekhaeil/logging-variables-in-chrome-devtools-using-logpoints-5fae</guid>
      <description>&lt;p&gt;&lt;code&gt;logpoints&lt;/code&gt; in Chrome DevTools allow you insert logging statements without adding breakpoints.&lt;/p&gt;

&lt;p&gt;Right-click on the line you want to log:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Je-pua3H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/4616064/170044768-23b8fc0e-7f97-4452-9089-0be65fd2a0c2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Je-pua3H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/4616064/170044768-23b8fc0e-7f97-4452-9089-0be65fd2a0c2.png" alt="Screenshot 2022-05-24 at 9 19 45 PM" width="756" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add the statement you would like to output to the console:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--whBVMegn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/4616064/170045738-4ea2c14f-70b4-4e59-a907-f82487e610f6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--whBVMegn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/4616064/170045738-4ea2c14f-70b4-4e59-a907-f82487e610f6.png" alt="Screenshot 2022-05-24 at 9 23 58 PM" width="793" height="224"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Everytime the code runs on this &lt;code&gt;logpoint&lt;/code&gt;, it will output to the console:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nKgFmzxi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/4616064/170046595-7a2309f2-5733-49ec-9ba0-7e989c01eb6a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nKgFmzxi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/4616064/170046595-7a2309f2-5733-49ec-9ba0-7e989c01eb6a.png" alt="Screenshot 2022-05-24 at 9 27 54 PM" width="755" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This allows for quick console logging without having you to touch your source code and without having to add breakpoints.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Today I Learned&lt;/strong&gt;&lt;br&gt;
I share what I learn on &lt;a href="https://petermekhaeil.com/today-i-learned/"&gt;Today I Learned&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>Svelte components have file location meta data</title>
      <dc:creator>Peter Mekhaeil</dc:creator>
      <pubDate>Sat, 07 May 2022 01:29:55 +0000</pubDate>
      <link>https://forem.com/petermekhaeil/svelte-components-have-file-location-meta-data-1h0c</link>
      <guid>https://forem.com/petermekhaeil/svelte-components-have-file-location-meta-data-1h0c</guid>
      <description>&lt;p&gt;Svelte nodes have a &lt;code&gt;__svelte_meta&lt;/code&gt; object in development mode that contains the file location of the component that rendered that node.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"loc"&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;"file"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/routes/index.svelte"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"line"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"column"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"char"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;358&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You an try it out on &lt;a href="https://node.new/sveltekit"&gt;StackBlitz&lt;/a&gt;. Inspect an element using the Chrome Dev Tools and use the console:&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;$0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;__svelte_meta&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Dfecg5oy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/4616064/167232485-a712022b-b799-441a-a052-70f2a5ff9633.png" class="article-body-image-wrapper"&gt;&lt;img alt="Screenshot 2022-05-07 at 9 25 47 AM" src="https://res.cloudinary.com/practicaldev/image/fetch/s--Dfecg5oy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/4616064/167232485-a712022b-b799-441a-a052-70f2a5ff9633.png" width="880" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(&lt;code&gt;$0&lt;/code&gt; &lt;a href="https://developer.chrome.com/docs/devtools/console/utilities/#recent-many"&gt;references the last selected DOM element&lt;/a&gt;. It is part of the DevTool's Console API)&lt;/p&gt;

&lt;p&gt;Learn more about &lt;code&gt;__svelte_meta&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/sveltejs/svelte/pull/1501"&gt;https://github.com/sveltejs/svelte/pull/1501&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sveltejs/svelte/issues/1499"&gt;https://github.com/sveltejs/svelte/issues/1499&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Today I Learned&lt;/strong&gt;&lt;br&gt;
I share what I learn on &lt;a href="https://petermekhaeil.com/today-i-learned/"&gt;Today I Learned&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>svelte</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>Using GitHub Actions to push changes</title>
      <dc:creator>Peter Mekhaeil</dc:creator>
      <pubDate>Sat, 16 Apr 2022 11:10:55 +0000</pubDate>
      <link>https://forem.com/petermekhaeil/using-github-actions-to-push-changes-1om</link>
      <guid>https://forem.com/petermekhaeil/using-github-actions-to-push-changes-1om</guid>
      <description>&lt;p&gt;We can use &lt;a href="https://docs.github.com/en/actions"&gt;GitHub Actions&lt;/a&gt; to push a new commit each time there is a new change detected. &lt;/p&gt;

&lt;p&gt;I've recently had to do this to automate updating &lt;code&gt;README.md&lt;/code&gt; with a listing of the repository files each time a new push has been detected. The GitHub workflow is found &lt;a href="https://github.com/petermekhaeil/til/blob/master/.github/workflows/update.yml"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here are the learnings that may come useful to others:&lt;/p&gt;

&lt;h2&gt;
  
  
  Using bash to clear file content
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /dev/null &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;/dev/null&lt;/code&gt; is a pseudo file in Linux that has no output so we can override a file with this empty content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using bash to echo a string with new line
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'# Today I Learned\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;-e&lt;/code&gt; is required to escape backslashes. This allows us to print new line (&lt;code&gt;\n&lt;/code&gt;) when outputting to a file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using bash to read the first line of a file
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 1 &lt;span class="nv"&gt;$filename&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using bash to remove characters from a string
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'# Title'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/# //'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;sed&lt;/code&gt; is short for &lt;code&gt;Stream EDitor&lt;/code&gt; and one of its common uses is pattern replacement. The above will remove &lt;code&gt;#&lt;/code&gt; from the string (by replacing it with an empty string).&lt;/p&gt;

&lt;h2&gt;
  
  
  Using bash to append string to file
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'My string'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; operator is used to append to a file (or create the file if it does not exist).&lt;/p&gt;

&lt;h2&gt;
  
  
  Using GitHub Actions to push changes to a repository
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/actions/checkout"&gt;actions/checkout&lt;/a&gt; is an offical GitHub Action that can checkout a repository. We can also use this to &lt;a href="https://github.com/actions/checkout#Push-a-commit-using-the-built-in-token"&gt;push changes back&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="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;push&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&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/checkout@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&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;# Clear README.md&lt;/span&gt;
          &lt;span class="s"&gt;cat /dev/null &amp;gt; README.md&lt;/span&gt;

          &lt;span class="s"&gt;# Add Title&lt;/span&gt;
          &lt;span class="s"&gt;echo -e '# Today I Learned\n' &amp;gt; README.md&lt;/span&gt;

          &lt;span class="s"&gt;# Loop through all TILs and add to README.md&lt;/span&gt;
          &lt;span class="s"&gt;dir=./learnings&lt;/span&gt;
          &lt;span class="s"&gt;for filename in "$dir"/*&lt;/span&gt;
          &lt;span class="s"&gt;do&lt;/span&gt;
            &lt;span class="s"&gt;title=$(head -n 1 $filename | sed 's/# //')&lt;/span&gt;
            &lt;span class="s"&gt;echo "- [$title](https://github.com/petermekhaeil/til/blob/master/$filename)" &amp;gt;&amp;gt; README.md&lt;/span&gt;
          &lt;span class="s"&gt;done&lt;/span&gt;

          &lt;span class="s"&gt;# Push changes&lt;/span&gt;
          &lt;span class="s"&gt;git config user.name github-actions&lt;/span&gt;
          &lt;span class="s"&gt;git config user.email github-actions@github.com&lt;/span&gt;
          &lt;span class="s"&gt;git add README.md&lt;/span&gt;
          &lt;span class="s"&gt;git commit -m "Update README.md"&lt;/span&gt;
          &lt;span class="s"&gt;git push&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Today I Learned&lt;/strong&gt;&lt;br&gt;
I share what I learn on &lt;a href="https://petermekhaeil.com/today-i-learned/"&gt;Today I Learned&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>github</category>
      <category>git</category>
      <category>bash</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>Measuring the performance of the McLaren Racing website</title>
      <dc:creator>Peter Mekhaeil</dc:creator>
      <pubDate>Sat, 19 Mar 2022 14:55:07 +0000</pubDate>
      <link>https://forem.com/petermekhaeil/measuring-the-performance-of-the-mclaren-racing-website-97k</link>
      <guid>https://forem.com/petermekhaeil/measuring-the-performance-of-the-mclaren-racing-website-97k</guid>
      <description>&lt;p&gt;With &lt;a href="https://www.mclaren.com/racing/"&gt;McLaren Racing&lt;/a&gt; recently &lt;a href="https://twitter.com/McLarenF1/status/1504133329855164420"&gt;announced a partnership&lt;/a&gt; with Google Chrome, this is a great opportunity for McLaren to measure the performance of their website and look into how it can be improved. Today, we will look at their performance scores and talk about some possible improvements that McLaren can do to their website performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the McLaren Racing website will be measured
&lt;/h2&gt;

&lt;p&gt;We will use a couple tools to measure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://f1-page-speed-insights.netlify.app/"&gt;F1 Page Speed Insights&lt;/a&gt; - an online tool that aggregate the scores and compares the websites of the teams involved in Formula 1.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developers.google.com/speed/docs/insights/v5/about"&gt;PageSpeed Insights&lt;/a&gt; is an online tool that reports on site performance and it includes both &lt;a href="https://developers.google.com/web/fundamentals/performance/speed-tools/#lab_data"&gt;lab data&lt;/a&gt; and &lt;a href="https://developers.google.com/web/fundamentals/performance/speed-tools/#field_data"&gt;real-world field data&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.webpagetest.org/"&gt;WebPageTest&lt;/a&gt; can test websites from different locations around the world using different browsers and can provide in-depth analysis in a site’s performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An earlier post was written on the page speed performance of Formula 1 websites which explains in details how web performance scores are measured. Read “&lt;a href="https://petermekhaeil.com/page-speed-performance-of-formula-1-websites/"&gt;Page Speed Performance of Formula 1 Websites&lt;/a&gt;”.&lt;/p&gt;

&lt;h2&gt;
  
  
  The performance score of the McLaren Racing website
&lt;/h2&gt;

&lt;p&gt;Using &lt;a href="https://f1-page-speed-insights.netlify.app/"&gt;F1 Page Speed Insights&lt;/a&gt; we can see how McLaren Racing is doing against the other teams. Lots of room for improvement if McLaren wants podium wins this year.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2AKis8hY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://petermekhaeil.com/images/uploads/mclaren-0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2AKis8hY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://petermekhaeil.com/images/uploads/mclaren-0.png" alt="Performance Score" width="880" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The detailed breakdown on McLaren Racing’s website shows that it did not pass the &lt;a href="https://web.dev/vitals/"&gt;Core Web Vitals&lt;/a&gt; assessment. We will look at some possible improvements that can bump the score up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yWSLnYZO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://petermekhaeil.com/images/uploads/mclaren-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yWSLnYZO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://petermekhaeil.com/images/uploads/mclaren-1.png" alt="Performance Score" width="880" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The current state of the McLaren Racing website
&lt;/h2&gt;

&lt;p&gt;The filmstrip generated by &lt;a href="https://www.webpagetest.org/"&gt;WebPageTest&lt;/a&gt; is a good tool to see the rough timing of elements as they start to appear on the page. Here we can see the main background starts to load around the 6 second mark.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QZwI_-9V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://petermekhaeil.com/images/uploads/mclaren-1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QZwI_-9V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://petermekhaeil.com/images/uploads/mclaren-1.gif" alt="McLaren Filmstrip Video" width="408" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;WebPageTest also provides a waterfall of the network requests made by the page. We can use this to understand the sequence of the requests and their timings:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--h9xCBGBL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://petermekhaeil.com/images/uploads/mclaren-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--h9xCBGBL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://petermekhaeil.com/images/uploads/mclaren-2.png" alt="McLaren Waterfall Chart" width="880" height="1179"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Possible Improvements to the McLaren Racing website
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Avoid blocking resources on other domains&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Row 5 of the waterfall &lt;a href="https://web.dev/render-blocking-resources/"&gt;shows a render-blocking CSS&lt;/a&gt; that is loaded in the head of the page. The thin line at the start of the request is the connection setup because it is located on a different domain. We can prepare this connection set up earlier by using &lt;code&gt;&amp;lt;link rel="preload"/&amp;gt;&lt;/code&gt; to initiate the download sooner.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ykWgRHw0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://petermekhaeil.com/images/uploads/mclaren-3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ykWgRHw0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://petermekhaeil.com/images/uploads/mclaren-3.png" alt="McLaren Waterfall Chart" width="880" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Learn more about how to &lt;a href="https://web.dev/preload-critical-assets/"&gt;preload critical assets to improve loading speed&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Avoid loading unused CSS
&lt;/h3&gt;

&lt;p&gt;A deeper look into the CSS from above and we can see that 94.7% of it is unused on the landing page - it is render-blocking and mostly unused on this page. We can cut a lot of the 500ms if we only load the CSS required on this page (we can also just remove this CSS file entirely and inline the small amount of CSS that was used).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3siDLKAK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://petermekhaeil.com/images/uploads/mclaren-4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3siDLKAK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://petermekhaeil.com/images/uploads/mclaren-4.png" alt="McLaren Unused CSS" width="880" height="90"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Learn more about &lt;a href="https://web.dev/unused-css-rules"&gt;removing unused CSS&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preconnect for early connection setup
&lt;/h3&gt;

&lt;p&gt;The McLaren Racing website loads the assets from different domains which has a connection cost (indicated by the thin line that appears before the requests in the waterfall chart). We can &lt;code&gt;preconnect&lt;/code&gt; to those domains to let the browser know we intend to make a request to those domains and so the browser will prepare the connection early and avoid having to do it later when we make the requests:&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;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"preconnect"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://static-cdn.mclaren.com"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"preconnect"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://media-cdn.mclaren.com"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Learn more about &lt;a href="https://web.dev/preconnect-and-dns-prefetch/"&gt;establishing network connections early to improve perceived page speed&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Avoid blocking parsing with JavaScript
&lt;/h3&gt;

&lt;p&gt;The JavaScript file on Row 6 is parser-blocking. It is located in the body in the document and the browser has decided to parse and execute this JavaScript file before it attempts to download the other assets on the page. This can be seen on this waterfall. Row 35 is the main hero background image which has a “wait” time because the browser was waiting for Row 6 to finish executing. This was also seen in the filmstrip video above. By using the &lt;code&gt;defer&lt;/code&gt; or &lt;code&gt;async&lt;/code&gt; attribute on the JavaScript file on Row 6, 7 and 8, we can remove the render-blocking and the rest of the images on the page can be parsed earlier. And in return, the main background image may possibly load earlier which will bring up the Core Web Vital score. A good illustration on how these attributes work can be found on &lt;a href="https://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html"&gt;async vs defer attributes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LMhoyH32--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://petermekhaeil.com/images/uploads/mclaren-5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LMhoyH32--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://petermekhaeil.com/images/uploads/mclaren-5.png" alt="McLaren Waterfall Chart" width="880" height="1188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Learn more about &lt;a href="https://web.dev/render-blocking-resources/"&gt;eliminating render-blocking resources&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimise images for the web
&lt;/h3&gt;

&lt;p&gt;The biggest opportunity to improve the McLaren Racing website could be in optimising the large images that they are serving. Here is a screenshot from Google Lighthouse pointing out the possible savings if we are to optimise the images:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eXjh5_dV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://petermekhaeil.com/images/uploads/mclaren-6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eXjh5_dV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://petermekhaeil.com/images/uploads/mclaren-6.png" alt="McLaren Google Lighthouse" width="880" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The images on the McLaren Racing website aren’t optimised for the web and there is over 13MB worth of these large high-quality images. We can use &lt;a href="https://squoosh.app/"&gt;Squoosh&lt;/a&gt; to reduce the file size of these images while maintaining quality.&lt;/p&gt;

&lt;p&gt;Doing a quick Squoosh run on the top 3 sized images, there is a cost savings of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Samsung-Galaxy-S9-v3.png: 805.4 KB -&amp;gt; 88.4 KB &lt;strong&gt;(-89.0%)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;mclarenplus.jpg: 240.1 KB -&amp;gt; 36.3 KB &lt;strong&gt;(-84.9%)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;2022-drive-to-survive-4.jpg: 161.1 KB -&amp;gt; 104.5 KB &lt;strong&gt;(-35.1%)&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using AVIF format we can even reduce the size even further. AVIF is not supported on all browsers but we can progressively support those that do:&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;picture&amp;gt;&lt;/span&gt; 
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/avif"&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"img.avif"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt; 
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"img.jpg"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Learn more about &lt;a href="https://web.dev/uses-webp-images/"&gt;serving images in modern formats&lt;/a&gt; and &lt;a href="https://web.dev/uses-optimized-images/"&gt;efficiently encoding images&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lazy load images that are below-the-fold
&lt;/h3&gt;

&lt;p&gt;Below-the-fold is a term that describes the area of the website that the user cannot see unless they scroll down. Most of the images on the McLaren Racing website are below-the-fold and they are being downloaded even though they are not in view. We can avoid the requests being made by lazy loading those images using &lt;code&gt;&amp;lt;img loading="lazy" /&amp;gt;&lt;/code&gt; so they are only requested as the user scrolls closer to those images. This will improve performance and save bandwidth.&lt;/p&gt;

&lt;p&gt;Learn more about &lt;a href="https://web.dev/lazy-loading-images/"&gt;lazy-loading images&lt;/a&gt; and &lt;a href="https://web.dev/browser-level-image-lazy-loading/"&gt;browser-level image lazy-loading for the web&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary of opportunities
&lt;/h2&gt;

&lt;p&gt;Recapping the key focus areas that McLaren Racing can take advantage of to improve their web performance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoid blocking resources on other domains.&lt;/li&gt;
&lt;li&gt;Avoid loading unused CSS.&lt;/li&gt;
&lt;li&gt;Preconnect for early connection setup.&lt;/li&gt;
&lt;li&gt;Avoid blocking parsing with JavaScript.&lt;/li&gt;
&lt;li&gt;Optimise images for the web.&lt;/li&gt;
&lt;li&gt;Lazy load images that are below-the-fold.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>performance</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>css</category>
    </item>
    <item>
      <title>How to build an app with Remix and Netlify Graph</title>
      <dc:creator>Peter Mekhaeil</dc:creator>
      <pubDate>Thu, 03 Mar 2022 00:00:00 +0000</pubDate>
      <link>https://forem.com/petermekhaeil/how-to-build-an-app-with-remix-and-netlify-graph-554k</link>
      <guid>https://forem.com/petermekhaeil/how-to-build-an-app-with-remix-and-netlify-graph-554k</guid>
      <description>&lt;p&gt;In this tutorial, you will learn how to build an app using Remix and use Netlify Graph to talk to third-party APIs. We will use Netlify Graph to fetch data from GitHub and show the user a collection of repository issues. Along the way we will learn about the tools chosen and why they are a great fit for our use case.&lt;/p&gt;

&lt;h3&gt;
  
  
  Source code and demo
&lt;/h3&gt;

&lt;p&gt;The source code for this app can be found &lt;a href="https://github.com/petermekhaeil/netlify-graph-remix"&gt;in this repository&lt;/a&gt;. Add an issue to the repository (or add a comment to an existing issue) to see the changes get reflected instantly on the &lt;a href="https://netlify-graph-remix.netlify.app/"&gt;demo app&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Remix
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://remix.run/"&gt;Remix&lt;/a&gt; is a full stack web framework that let's you focus on the user interface and work back through web fundamentals to deliver a fast, slick, and resilient user experience. Remix is great for our use case because it generates fast responding pages on the server so we can get the latest data from GitHub each time the user hits our pages.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Netlify Graph
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/netlify/labs/tree/main/features/graph/documentation"&gt;Netlify Graph&lt;/a&gt; provides developers with a GraphQL-based approach to integrating with third-party APIs without needing to operate a seperate GraphQL server. Developers do not need to spend time learning the API structure of each provider and writing code to piece it together. This is great for our use case because we do not want to worry about how to authenticate with GitHub as Netlify will handle this for us.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting Started with Remix
&lt;/h3&gt;

&lt;p&gt;Create a new Remix site. Choose "Netlify" when prompted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-remix@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install dependencies
&lt;/h3&gt;

&lt;p&gt;We will need to install some dependencies for our app to work. Change directory to your new project and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;slugify marked
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;slugify&lt;/strong&gt; : Generates URL-friendly paths based on a text string. We will use to generate URLs based on the title of our GitHub issues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;marked&lt;/strong&gt; : Markdown parser to convert Markdown to HTML. We will use it to render the GitHub issue body onto our HTML page.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Create a GitHub Repository
&lt;/h3&gt;

&lt;p&gt;You will want to &lt;a href="https://github.com/new"&gt;create a new repository in GitHub&lt;/a&gt; and commit the code that was generated in the earlier step. This will be the repository that we will be fetching data from using Netlify Graph.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;name of project]
git init
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"first commit"&lt;/span&gt;
git remote add origin https://github.com/your-username/name-of-project.git
git add push &lt;span class="nt"&gt;-u&lt;/span&gt; origin master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install the Netlify CLI
&lt;/h3&gt;

&lt;p&gt;Install the latest version of Netlify CLI and log into your Netlify account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i -g netlify-cli@latest
ntl login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change directory to your new project (if you have not done so already) and create a new Netlify site:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd [name of project]
ntl lnit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Start Development Server
&lt;/h3&gt;

&lt;p&gt;Start the Netlify Dev Server with the &lt;code&gt;--graph&lt;/code&gt; flag:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This will start up a local development server that we will use to build our app. It will also start up a Netlify Graph session that we will use in the next section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a GraphQL query with the Graph Explorer
&lt;/h3&gt;

&lt;p&gt;Select your site from your &lt;a href="https://app.netlify.com/"&gt;Netlify team dashboard&lt;/a&gt; and then select “Graph” from your Netlify site dashboard menu. Click on "Connect API or service".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bgcG8WhF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/4616064/154813791-216f4532-70aa-4b2f-aedd-3b8e26d260e9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bgcG8WhF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/4616064/154813791-216f4532-70aa-4b2f-aedd-3b8e26d260e9.png" alt="Netlify Graph Dashboard" width="880" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Netlify Graph supports a handful of API providers that we can connect with:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mFozzyVY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://petermekhaeil.com/images/uploads/netlify-remix-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mFozzyVY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://petermekhaeil.com/images/uploads/netlify-remix-2.png" alt="Netlify Graph Dashboard" width="754" height="755"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are going to connect to GitHub. Select "GitHub" from the list of providers and you will need to enable both "Authentication" and "Graph Explorer". Then continue with "Start querying GitHub":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MF9D2nin--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://petermekhaeil.com/images/uploads/netlify-remix-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MF9D2nin--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://petermekhaeil.com/images/uploads/netlify-remix-1.png" alt="Netlify Graph Dashboard" width="711" height="584"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you have selected your open session (that was created when you started the dev server), you will be presented with a Graph Explorer. This will allow you to query the GitHub API for the data you are interested in fetching. For our use case, we will want to fetch the GitHub repository issues and their comments.&lt;/p&gt;

&lt;p&gt;Below is a GraphQL query with &lt;code&gt;Issues&lt;/code&gt; as its operation name. Copy the below snippet and paste it in the explorer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Issues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&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="s2"&gt;"petermekhaeil"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&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="nb"&gt;String&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="s2"&gt;"netlify-graph-remix"&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;gitHub&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;repository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$owner&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;issues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;states&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OPEN&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;edges&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;node&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;createdAt&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="n"&gt;createdAt&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="n"&gt;author&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;avatarUrl&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After clicking "Save Changes", you can head back to your IDE and you will notice some changes that have been made to your workspace. A new &lt;code&gt;netlifyGraph&lt;/code&gt; folder has appeared:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;netlify/functions/netlifyGraph/index.d.ts
netlify/functions/netlifyGraph/index.js
netlify/functions/netlifyGraph/netlifyGraphOperationsLibrary.graphql
netlify/functions/netlifyGraph/netlifyGraphSchema.graphql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Netlify Graph has generated code for us to use to fetch the data from our app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fetching Data from Remix
&lt;/h3&gt;

&lt;p&gt;The Remix app we created earlier came with a demo index page. We can remove it and replace the content of &lt;code&gt;./app/routes/index.jsx&lt;/code&gt; with the code below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useLoaderData&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;remix&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;slugify&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;slugify&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;NetlifyGraph&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;../../netlify/functions/netlifyGraph&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;const&lt;/span&gt; &lt;span class="nx"&gt;loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="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="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;NetlifyGraph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetchIssues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;petermekhaeil&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;netlify-graph-remix&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;accessToken&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;ONEGRAPH_AUTHLIFY_TOKEN&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gitHub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;edges&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;slugify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toLowerCase&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Index&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useLoaderData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Issues&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&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;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Walking through the code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;loader&lt;/code&gt; function is a built-in data loading Remix API. Each Remix route is served from the server and each route can fetch external data before generating the HTML and returning it back to the user. It is in this function that we will fetch data from Netlify Graph and use it in the React template.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;NetlifyGraph.fetchIssues&lt;/code&gt; is the generated function from Netlify Graph that we will use to fetch the data from GitHub. You can replace the value of &lt;code&gt;owner&lt;/code&gt; and &lt;code&gt;name&lt;/code&gt; to your own repository, or use the default value if you wish.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;useLoaderData&lt;/code&gt; hook from Remix will return the data from the &lt;code&gt;loader&lt;/code&gt; function so we can use it in the React template.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Refresh your browser and you will see Remix rendering the list of issues from the GitHub repository.&lt;/p&gt;

&lt;p&gt;There are two things we should note from the above code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Authentication will be taken care by Netlify regardless of which API provider you decide to work with. This is an awesome feature of Netlify Graph because it allows us not to worry about the structures of the third-party APIs. Netlify takes care of this by handling the value of the &lt;code&gt;ONEGRAPH_AUTHLIFY_TOKEN&lt;/code&gt; environment variable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Netlify Graph generated the JS function with the name &lt;code&gt;fetchIssues&lt;/code&gt; because the GraphQL query that we wrote had the operation name as &lt;code&gt;Issues&lt;/code&gt;. We can have many operations in the GraphQL Explorer and Netlify Graph will generate a JS function for each one with unique names based on the operation name.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Dynamic Routes in Remix
&lt;/h3&gt;

&lt;p&gt;Instead of creating each page manually by hand, Remix can create a route for each of the GitHub issue dynamically.&lt;/p&gt;

&lt;p&gt;We can create a dynamic route by creating &lt;code&gt;./app/routes/$slug.jsx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useLoaderData&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;remix&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;NetlifyGraph&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;../../netlify/functions/netlifyGraph&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;slugify&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;slugify&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;marked&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;marked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getLoaderData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;NetlifyGraph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetchIssues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;petermekhaeil&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;netlify-graph-remix&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;accessToken&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;ONEGRAPH_AUTHLIFY_TOKEN&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;post&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;gitHub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;edges&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;slugify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;node&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;loader&lt;/span&gt; &lt;span class="o"&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;params&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;post&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;getLoaderData&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;slug&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;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;marked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&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;post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;slug&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;slug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;html&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;PostSlug&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;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useLoaderData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;dangerouslySetInnerHTML&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;__html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Walking through the code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;getLoaderData&lt;/code&gt; will fetch the GitHub data via Netlify Graph and filter for the GitHub issue we want by the value of &lt;code&gt;title&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We have been introduced to &lt;code&gt;params&lt;/code&gt; in the &lt;code&gt;loader&lt;/code&gt; function. &lt;code&gt;params&lt;/code&gt; contain the dynamic key that is attached after the &lt;code&gt;$&lt;/code&gt; in the filename. In our example, the filename &lt;code&gt;$slug.jsx&lt;/code&gt; will return &lt;code&gt;params.slug&lt;/code&gt; with the value from the URL. We will use this value as the title of the GitHub issue we want to display on this page.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now if you refresh the page and click on any of the GitHub issues, you'll be redirected to a page with the full issue content. An example of a dynamic route from the demo app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://netlify-graph-remix.netlify.app/getting-started-with-remix-and-netlify-graph"&gt;/getting-started-with-remix-and-netlify-graph&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What we learnt today
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Each Remix route supports a &lt;a href="https://remix.run/docs/en/v1/api/conventions#loader"&gt;loader&lt;/a&gt; function that is called on the server before rendering. It provides data to the route and can be used in the React template.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Remix supports &lt;a href="https://remix.run/docs/en/v1/api/conventions#dynamic-route-parameters"&gt;dynamic routes&lt;/a&gt; by using the &lt;code&gt;$&lt;/code&gt; filename convention.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://remix.run/docs/en/v1/tutorials/jokes#expected-errors"&gt;Expected errors&lt;/a&gt; can be caught in Remix and we can render user-friendly messages using a &lt;a href="https://remix.run/docs/en/v1/api/conventions#catchboundary"&gt;CatchBoundary&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Netlify Graph uses a Graph Explorer to query the data we need and is synced with the session opened by the CLI.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Netlify Graph generates code that we can use to fetch data from a third-party API without needing to worry about authentication or understanding the API structure.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>netlify</category>
      <category>remix</category>
      <category>graphql</category>
    </item>
    <item>
      <title>How I landed my first job as a software engineer</title>
      <dc:creator>Peter Mekhaeil</dc:creator>
      <pubDate>Fri, 28 Jan 2022 00:00:00 +0000</pubDate>
      <link>https://forem.com/petermekhaeil/how-i-landed-my-first-job-as-a-software-engineer-1lhj</link>
      <guid>https://forem.com/petermekhaeil/how-i-landed-my-first-job-as-a-software-engineer-1lhj</guid>
      <description>&lt;p&gt;I was once an entry level developer with no experience, looking for my first door into an engineering career. I finished my studies back in 2009 and for almost a year I could not find a job. It almost felt like a blind journey from one interview to another, that all ended up in rejection because of lack of experience and knowledge. Without knowing where to look or without knowing what to research, I was mostly answering interview questions with answers that I thought was right, based on my limited knowledge in web development (my computer science studies did not include web dev subjects).&lt;/p&gt;

&lt;p&gt;It was until my approach changed during an interview at what soon to become the first company that accepted me.&lt;/p&gt;

&lt;p&gt;At the very start of the interview, without any thought behind it - I told the engineering manager something I’ve never said during my previous interviews: I said something along the lines of &lt;em&gt;“Before we continue, I want to tell you - I do not know anything. But I want to learn”&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Everything changed from that moment. The manager continued the interview by listing their tech stack and asked if any of it sounds familiar, and my answer was no to all of them. Nevertheless, he did not mind because I showed my eagerness to learn. The interview turned into a crash course introduction to the technologies and soon after, I was accepted for the job.&lt;/p&gt;

&lt;p&gt;Call out your area of focus so others are aware that you are know. Shawn calls this &lt;a href="https://www.swyx.io/lampshading/"&gt;the Power of Lampshading&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For entry level developers looking to land their first job as a software engineer, it is totally okay to call out your area of focus. Let the interviewers know that you do not know the answers and you are willing to learn. This is powerful in itself and calling out your focus areas will have an impact throughout your career.&lt;/p&gt;

</description>
      <category>career</category>
      <category>leadership</category>
    </item>
  </channel>
</rss>
