<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: John Grisham</title>
    <description>The latest articles on DEV Community by John Grisham (@squashbugler).</description>
    <link>https://dev.to/squashbugler</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%2F485305%2F21c3b64d-ce9a-498b-90f3-58168f114b33.jpg</url>
      <title>DEV Community: John Grisham</title>
      <link>https://dev.to/squashbugler</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/squashbugler"/>
    <language>en</language>
    <item>
      <title>How I Automated My Weekly Newsletter using BlitzJs and CRON</title>
      <dc:creator>John Grisham</dc:creator>
      <pubDate>Tue, 24 Aug 2021 17:00:32 +0000</pubDate>
      <link>https://dev.to/squashbugler/how-i-automated-my-weekly-newsletter-using-blitzjs-and-cron-96p</link>
      <guid>https://dev.to/squashbugler/how-i-automated-my-weekly-newsletter-using-blitzjs-and-cron-96p</guid>
      <description>&lt;p&gt;I recently discovered Blitz.js and love how quickly the framework lets you build an application but my favorite part is the command-line tool. Blitz.js has a console that can be used to run predefined queries and mutations via a node REPL environment which is great for manually running tasks from the command line, but less good when ran inside of CRON job since it will take over whatever process it’s running on.&lt;/p&gt;

&lt;p&gt;I have a newsletter that collects news stories and I wanted to automate sending this newsletter every other week. I needed a CRON solution initiated via a bash script that wasn’t using expect commands so I started looking into the child_process module.&lt;/p&gt;

&lt;p&gt;To support me sign up for Medium and read this tutorial there: &lt;a href="https://john-grisham.medium.com/membership"&gt;Medium signup link&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Clearing old data
&lt;/h2&gt;

&lt;p&gt;Blitz.js is built upon Next.js so hosting is flexible but for me, the winner is Render.com. I’m not affiliated with them but of the hosting platforms I’ve tried, they have been the cheapest and easiest to set up. This post won’t go in-depth with how to use Render for hosting, just be aware that is the host where I set up my CRON job.&lt;/p&gt;

&lt;p&gt;Render offers a simple CRON service. I want mine to do two things once a week.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Delete records from my database that are more than 6 days old&lt;/li&gt;
&lt;li&gt;Run my newsletter auto-mail mutation if the week is odd (every other week)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My bash script will be the entry point and will initiate these processes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo 'Running weekly script...'
echo 'Clearing old data...'
chmod +x ./automation/clearMail.ts
ts-node ./automation/clearMail.ts
echo 'Old data cleared from db'
echo 'Checking if auto mailer should send...'
WEEK=`date +"%V"`
if [ $(($WEEK%2)) -eq 0 ];
then
    echo "Sending auto mailer..."
    chmod +x ./automation/sendAutoMail.ts
    ts-node ./automation/sendAutoMail.ts
else
    echo "No auto mail to send"
fi
echo 'Done'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I use ts-node to make calls to other Typescript files here which will execute the core logic that I want to accomplish. Lets take a look at the first one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// clearMail.ts
import { onExit, streamEnd } from '@rauschma/stringio'
import { spawn } from 'child_process'
import { writeToWritable } from '../app/core/utils'

async function clearMail() {
    const blitzProcess = spawn('blitz console', [], { shell: true, stdio: ['pipe', process.stdout, process.stderr] })
    blitzProcess.on('error', (err) =&amp;gt; {
        console.error(err.message)
    })

    await writeToWritable({
        commands: ['await clearOld()', '.exit'],
        writable: blitzProcess.stdin
    })

    return await onExit(blitzProcess)
}

clearMail()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file will spawn a new process using the blitz console with a custom stdio configuration. This configuration is important and is the only one that worked for my solution. The problem I kept running into was that once the blitz console started it would gum up my main process and prevent me from running further commands inside the console. We are making a way for the parent process to communicate with the child process input. The parent can tell the child what to do and vice versa. As for the output and error channel, we make those default to the main process stout and sterr respectively.&lt;/p&gt;

&lt;p&gt;I also tell the blitz process to log any errors via the process on error event handler. I’m using some utility methods to then run my clearOld mutation that will delete old records from my mail database. I’m also using the stringio package from Axel Rauschmayer for managing the console execution stream.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// writeToWritable.ts
import { streamEnd, streamWrite } from '@rauschma/stringio'
import { Writable } from 'stream'

export default async function writeToWritable({ commands, writable }: { writable: Writable; commands: string[] }) {
    for (const cmd of commands) {
        await streamWrite(writable, `${cmd}\n`)
    }

    return await streamEnd(writable)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file will write multiple commands to the new child process and then end the stream. Once we’re done we’ll exit the blitz console child process. In case you’re interested my clearOld mutation looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// clearOld.ts
import db from 'db'
import { sub } from 'date-fns'

export default async function clearOld() {
    try {
        return await db.mail.deleteMany({
            where: {
                createdAt: {
                    lt: sub(new Date(), { days: 6 })
                }
            }
        })
    } catch (err) {
        throw new Error(err.message)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sending the newsletter
&lt;/h2&gt;

&lt;p&gt;The next part of the bash script runs the automatic mailer file every other week. This file will get my signups from Firebase and send them a collection of news stories via the Sendgrid API. The child process setup is the same here the only difference is that I have some arguments that I’d like to be able to pass when I run the file for this I use the pirate-themed command-line arguments package Yargs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/yargs/yargs"&gt;Yargs on Github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also have some utility methods for making a temp file with some HTML that I will then load when I send the newsletter. This HTML will serve as my template for the newsletter that I have saved in a different table. Anyway, all of that falls outside of the scope of this tutorial so here’s the base code for the auto mailer file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// sendAutoMail.ts
import { makeTemp, writeToWritable } from '../app/core/utils'
import { autoMailer } from '../designs/designs.json'
import { hideBin } from 'yargs/helpers'
import { onExit } from '@rauschma/stringio'
import { spawn } from 'child_process'
import yargs from 'yargs/yargs'

async function sendAutoMail() {
    try {
        const argv = yargs(hideBin(process.argv)).argv
        const isTest = (argv.test as boolean) ?? false
        const testUsers = isTest ? JSON.stringify([{ email: 'test@test.com' }]) : undefined
        const subject = isTest ? 'test' : 'Your weekly echo stories'
        const html = autoMailer
        const htmlName = await makeTemp({ data: html, name: 'auto_mailer' })
        const blitzProcess = spawn('blitz console', [], { shell: true, stdio: ['pipe', process.stdout, process.stderr] })

        blitzProcess.on('error', (err) =&amp;gt; {
            throw new Error(err.message)
        })

        blitzProcess.stdin.on('error', (err) =&amp;gt; {
            throw new Error(err.message)
        })

        if (testUsers !== undefined) {
            await writeToWritable({
                commands: [
                    `await sendMail({ html: '${htmlName}', raw: false, mail: true, subject: '${subject}', testUsers: ${testUsers}  })`,
                    '.exit'
                ],
                writable: blitzProcess.stdin
            })
        } else {
            await writeToWritable({
                commands: [`await sendMail({ html: '${htmlName}', raw: false, mail: true, subject: '${subject}'  })`, '.exit'],
                writable: blitzProcess.stdin
            })
        }

        return await onExit(blitzProcess)
    } catch (err) {
        throw new Error(err.message)
    }
}

sendAutoMail()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;I hope this has been helpful. Blitz.js is great and it’s a lot of fun to develop with but the CLI can also be used outside of generating files and folders which opens up some interesting CRON job solutions. I found the console to be simple to use and fun but lacking when trying to run commands automatically via bash. My solution is very specific to my use case and if I didn’t have such a particular goal in mind for my newsletter I would have used an existing email newsletter solution.&lt;/p&gt;

&lt;p&gt;This guide isn’t intended to be a step-by-step solution for your CRON endeavors with the blitz console but it should give you inspiration into what you can automate out of your workflow using these technologies. If you’re like me and you need solutions outside of what other Saas providers give, you can create your automated workflows using CRON and some elbow grease. What do you think of my solution? Is there room for improvement or have I overengineered it? If you’re curious you can also check out the newsletter that I built this CRON job for here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://echo-breaking-news.com"&gt;Echo Breaking News&lt;/a&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>blitzjs</category>
      <category>cron</category>
      <category>newsletter</category>
    </item>
    <item>
      <title>How to use typescript &amp; sails js for your REST API (Safer seas)</title>
      <dc:creator>John Grisham</dc:creator>
      <pubDate>Wed, 05 May 2021 17:51:46 +0000</pubDate>
      <link>https://dev.to/squashbugler/how-to-use-typescript-sails-js-for-your-rest-api-safer-seas-1la6</link>
      <guid>https://dev.to/squashbugler/how-to-use-typescript-sails-js-for-your-rest-api-safer-seas-1la6</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Shoutin', typing' an' screamin' — that be how we sail.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Have you ever seen pirates of the Caribbean? It's a Disney film series based on the exploits of various fictional pirate characters and pirate lore. The films make being a pirate sound cool as a sea cucumber but in reality the average life expectancy of a pirate was around 26 years, a dangerous life indeed.&lt;/p&gt;

&lt;p&gt;So why am I talking about pirates? My point is that freedom in life just like freedom in code is not always a good thing. That's why I love Typescript. Coding can be freeing but with that freedom comes a lot of danger. With Typescript troubled waters become much safer.&lt;/p&gt;

&lt;p&gt;I'm going to take you through my journey with Sails JS and Typescript so far and show you some tricks I've learned along the way. So let's get started!&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Typescript with Sails JS&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Sails doesn't use typescript by default but does support it. I won't dive very deep into getting started with Sails or integrating Typescript but you can configure a sails project with Typescript very easily by following the docs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sailsjs.com/documentation/tutorials/using-type-script"&gt;Using TypeScript&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;note&lt;/strong&gt; The docs will have you install ts-node, typescript and the necessary types as dependencies but make sure you install these as Dev dependencies only.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Creating Types&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;If you have a Sails project with Typescript ready you can follow along. In case you're interested here's my tsconfig:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "compilerOptions": {
        "target": "es6",
        "allowJs": true,
        "skipLibCheck": true,
        "strict": false,
        "forceConsistentCasingInFileNames": true,
        "noEmit": true,
        "esModuleInterop": true,
        "module": "commonjs",
        "moduleResolution": "node",
        "resolveJsonModule": true,
        "isolatedModules": false,
        "jsx": "preserve",
    "lib": ["es2018", "DOM", "ESNext"],
        "sourceMap": true,
        "baseUrl": "./",
        "outDir": "./build",
        "rootDir": "./"
    },
    "exclude": ["node_modules"],
    "include": ["./**/*.ts", "./**/*.tsx"]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're going to make a basic turtle type that we'll use in a minute. So go ahead and create an interfaces folder in api and inside it make a turtle.ts file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// api/interfaces/turtle.ts
export interface Turtle {
  shellColor: string
  age: number
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are just some basic props to illustrate how Typescript works. We'll use this type for the inputs in a standalone action. Similarly we will want to define our exits type as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// api/interfaces/exits.ts
export interface Exits {
  error: (response: Record&amp;lt;string, unknown&amp;gt;) =&amp;gt; void
  success: (response: Record&amp;lt;string, unknown&amp;gt;) =&amp;gt; void
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you aren't familiar with Typescript the Record type is sort-of like an object so what we're defining is two functions that will receive generic objects and return void. (But in reality both these functions will return a response from the action.)&lt;/p&gt;

&lt;p&gt;I also like to use an index file to manage my imports I recommend you do the same:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// api/interfaces/index.ts
export * from './exits'
export * from './turtle'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  &lt;strong&gt;Our first API endpoint&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;note&lt;/strong&gt;Going forward you may have to stop and restart your sails app to see these changes.&lt;/p&gt;

&lt;p&gt;It is now recommend by the developers at Sails to use the new Actions 2 format for actions. I like this format and I also like standalone actions because we don't have to explicitly define the routes to our endpoints, enabling automatic routes for standalone actions is easy. Just add this line in your config/blueprints.js file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;actions: true,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will allow our actions to be available at a logical location in our API by default. Go ahead and create a new standalone action by using the sails cli or by just copying the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; sails generate action turtles/sea
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will generate an action in api/controllers/turtles/sea, by default this is a .js file so rename it to a .ts file and replace the contents with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Exits, Turtle } from '../../interfaces'
import sails from 'sails'

module.exports = {

  friendlyName: 'Sea Turtles!',

  description: 'Turtles all the way down.',

  inputs: {},

  exits: {
    error: {
      message: 'Error!'
    },
    success: {
      data: null,
      message: 'success!'
    }
  },

  fn: async function (inputs: Turtle, exits: Exits) {
    exits.success({ message: 'success', data: inputs });
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigating to &lt;a href="http://localhost:8080/turtles/sea"&gt;localhost/turtles/sea&lt;/a&gt; should return this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "message": "success",
  "data": {}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Congrats you created a standalone action in Sails JS now it's time for some deep sea diving.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Generating Sails Schema's from Types&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;So we have a couple types and an action but you may have noticed something missing. While we have defined what the exits should look like in the action schema we haven't done so with the inputs. One problem I have with Actions 2 is that even though we gave types to our inputs and exits we still have to include them in the schema for Sails to understand what they are. If you were to try sending parameters to this endpoint they would get wiped and nothing would be returned.&lt;/p&gt;

&lt;p&gt;I decided to create a helper that generates a Sails JS schema from a Typescript type. That way we can keep our types in sync with our schema and we don't have to repeat ourselves. To do this we'll need help from this library.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/YousefED/typescript-json-schema"&gt;Typescript JSON schema&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can add it via yarn like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add typescript-json-schema
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above library will take a Type and spit out a plain JSON object that we can use for our inputs. Inside api/helpers create a new file called generate-schema.ts and paste the following code into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// api/helpers/generate-schema.ts
import { resolve } from "path";
import * as TJS from "typescript-json-schema";
import sails from 'sails'

interface GeneratorInputs {
  filePath: string
  name: string
}

interface GeneratorExits {
  success: (definition: TJS.Definition) =&amp;gt; TJS.Definition
}

const settings: TJS.PartialArgs = {
  required: true
};

const compilerOptions: TJS.CompilerOptions = {
  strictNullChecks: true,
};

module.exports = {
  friendlyName: 'Generate Schema',

  description: 'Generate schema from types!',

  sync: true,

  inputs: {

    filePath: {
      type: 'string',
      example: 'my-type.ts',
      description: 'The path to your type file.',
      required: true
    },

    name: {
      type: 'string',
      example: 'myType',
      description: 'The type name',
      required: true
    }

  },

  fn: function (inputs: GeneratorInputs, exits: GeneratorExits) {
    try {
      const typePath = resolve(`./api/interfaces/${inputs.filePath}`)
      sails.log.info(`generating inputs for type: ${inputs.name} at path: ${typePath}...`)

      const program = TJS.getProgramFromFiles(
        [typePath],
        compilerOptions
      )

      const schema = TJS.generateSchema(program, inputs.name, settings)

      return exits.success(schema)
    } catch (err) {
      throw new Error(`Could not generate types: ${err.message}`)
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The helper we just made will take a file path which is basically just the file name in this instance and a type name to create a program which will get passed to the generateSchema function. It's OK if you don't understand what's going on in the background with this library. When we're done we will either return the newly created schema or throw an error. The output will look something like this when we call it with the turtle type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "type": "object",
    "properties": {
      "shellColor": {
        "type": "string"
      },
      "age": {
        "type": "number"
      }
    },
    "required": [
      "age",
      "shellColor"
    ],
    "$schema": "http://json-schema.org/draft-07/schema#"
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Awesome! Right away we have a JSON object that has all our properties defined along with their type and whether or not they are required. However there are a few issues here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Properties is not necessary for the schema&lt;/li&gt;
&lt;li&gt;The required field needs to be on the property itself&lt;/li&gt;
&lt;li&gt;Type of "object" is not supported in Sails&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In order to solve these issues we'll need to parse and manipulate the generated schema. But to add a layer of complexity we will need to do so recursively since types can be nested.&lt;/p&gt;

&lt;p&gt;Finally, a recursive problem in programming that isn't inverting a binary tree!&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Turtles all the way down&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Let's make our Turtle type even more complex:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export interface Turtle {
  shellColor: string
  size: { length: number, weight?: number },
  age?: number
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll make age optional by adding the ? before the type and add a size prop with length and an optional weight prop, because it's rude to ask a turtle it's age or weight! Now that we have a more complex type to test let's create a format function and put it between the compiler options and the module exports.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const formatSchema = (obj: TJS.Definition) =&amp;gt; {
    const format = (layer: TJS.Definition) =&amp;gt; {
      const keys = Object.keys(layer)

      keys.forEach(key =&amp;gt; {
        if (key === "properties" || layer[key]?.hasOwnProperty("properties")) {
          let newProperties = {}
          let nextRequired = []

          if(key === "properties") {
            newProperties = Object.assign(layer, layer[key]);
            nextRequired = layer["required"]
            delete layer["type"]
            delete layer["required"]
            delete layer[key];
          } else {
            newProperties = Object.assign(layer[key], layer[key]["properties"]);
            nextRequired = layer[key]["required"] || []
            newProperties["required"] = layer["required"].includes(key)
            delete layer[key]["properties"];
          }

          if(newProperties["type"] === "object") {
             newProperties["type"] = "ref"
          }

          format({ ...newProperties, required: nextRequired  })
        } else if (key !== 'type' &amp;amp;&amp;amp; key !== 'required') {
          layer[key]["required"] = layer["required"]?.includes(key) || false
        }
      })

      return layer
  }

    delete obj.$schema
    return format(obj);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will go through each "layer" of the type iterate the keys and unwrap it's properties from the parent object. It will also determine if each property is required and what the type is. And since "ref" is the approximation of object in the Sails schema we'll replace references to object with "ref". As a last touch we'll delete the $schema prop since it's not needed. Now replace the call to create the schema with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const schema = formatSchema(TJS.generateSchema(program, inputs.name, settings))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now in sea.ts call the helper as an exit with the turtle type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;exits.success({ data: sails.helpers.generateSchema('turtle', 'Turtle') })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you visit &lt;a href="http://localhost:8080/turtles/sea"&gt;localhost:8080/turtles/sea&lt;/a&gt; you should see this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "data": {
    "shellColor": {
      "type": "string",
      "required": true
    },
    "size": {
      "type": "ref",
      "required": true,
      "length": {
        "type": "number",
        "required": true
      },
      "weight": {
        "type": "number",
        "required": false
      }
    },
    "age": {
      "type": "number",
      "required": false
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But of course we don't want to return the generated schema we want to use it as our inputs so replace the inputs with the helper instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;inputs: sails.helpers.generateSchema('turtle', 'Turtle'),
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And just return the inputs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;exits.success({ data: inputs })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you stop your Sails app and re-lift you'll see the generation log get called at our endpoint and just like that we have generated inputs and type safety!&lt;/p&gt;

&lt;p&gt;You can test it out by passing parameters for a turtle like so: &lt;a href="http://localhost:8080/turtles/sea?shellColor=brown&amp;amp;age=100&amp;amp;size=%7B%20length:%20%226ft%22,%20weight:%20%221000lbs%22%20%7D"&gt;Test Endpoint with Parameters&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;This concludes my introduction to Sails JS and Typescript.&lt;/p&gt;

&lt;p&gt;What we covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating interfaces&lt;/li&gt;
&lt;li&gt;Actions 2 and standalone actions&lt;/li&gt;
&lt;li&gt;Automatic routes for actions&lt;/li&gt;
&lt;li&gt;Generated Sails Schema&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;l like how easy it is to start using Typescript with Sails JS but I do wish Sails had type definitions. I would love to start adding them if there is enough interest. Let me know what you think about it here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/balderdashy/sails/issues/7110"&gt;Type definitions for Sails&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And for more tutorials about Sails, Pirates and programming follow me on Twitter &lt;a href="https://twitter.com/SquashBugler"&gt;@SquashBugler&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The quote at the beginning was generated at:&lt;a href="http://gangstaname.com/quotes/pirate?"&gt;The pirate quotes generator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And my favorite quote generated there.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I think that be his ol' poop deck. Blimey!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm a child, I know. Thanks for joining me and feel free to share your favorite pirate quotes in the comments.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>typescript</category>
      <category>api</category>
      <category>node</category>
    </item>
    <item>
      <title>How to Create Blog Posts From Markdown With Gatsby in 2021</title>
      <dc:creator>John Grisham</dc:creator>
      <pubDate>Fri, 26 Feb 2021 18:41:08 +0000</pubDate>
      <link>https://dev.to/squashbugler/how-to-create-blog-posts-from-markdown-with-gatsby-in-2021-35nb</link>
      <guid>https://dev.to/squashbugler/how-to-create-blog-posts-from-markdown-with-gatsby-in-2021-35nb</guid>
      <description>

&lt;p&gt;If you want to support me please check out the original post on Medium:&lt;br&gt;
&lt;a href="https://medium.com/@john-grisham/how-to-create-blog-posts-from-markdown-with-gatsby-in-2021-80636d6ca8e9"&gt;How to create blog posts from Markdown with Gatsby in 2021&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Let’s face it building a website is easier than ever; with plenty of platforms to choose from.&lt;/p&gt;

&lt;p&gt;But Regardless of where your website is hosted or the platform one thing is usually the same; blog posts.&lt;/p&gt;

&lt;p&gt;Then introduce Gatsby which is perfect for building static websites. And&lt;br&gt;
moving from another platform to Gatsby is easier when your blog posts are in Markdown which luckily they usually are!&lt;/p&gt;

&lt;p&gt;I’m going to show you how to take markdown files in Gatsby and turn them into generated HTML blog posts, so let’s get started.&lt;/p&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;Setting up the project&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;For this tutorial, I’m going to be using the free Peanut Butter &amp;amp; Jelly&lt;br&gt;
Gatsby template I created. The complete version is also available if you&lt;br&gt;
like the template and want to support me by purchasing it.&lt;/p&gt;

&lt;p&gt;You can check out the template demo here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://6019dc6ad647860008e0dc93--peanut-butter-jelly.netlify.app/"&gt;&lt;strong&gt;PB&amp;amp;J Demo&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And you can download it here:&lt;br&gt;
&lt;a&gt;&lt;/a&gt;&lt;a href="https://gum.co/pbj-template"&gt;&lt;strong&gt;PB&amp;amp;J Gumroad&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;or&lt;/p&gt;

&lt;p&gt;Clone the repo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/JohnGrisham/PB-JPlain.git"&gt;&lt;strong&gt;https://github.com/JohnGrisham/PB-JPlain.git&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will give you the same project to work from as the one I used to&lt;br&gt;
set up my landing page. To get this template up and running, in a&lt;br&gt;
terminal go into the directory you put the project in and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will download all the dependencies required to get going and once&lt;br&gt;
that’s done run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn develop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will start development and you should be able to navigate to&lt;br&gt;
&lt;a href="http://localhost:8000/"&gt;localhost:8000&lt;/a&gt; to see the landing page.&lt;/p&gt;

&lt;p&gt;If you haven’t done so already go ahead and open up the project in a&lt;br&gt;
text editor of your choice, I use Vscode.&lt;/p&gt;

&lt;p&gt;Take a few minutes to note the file structure, everything that’s&lt;br&gt;
included is documented in the readme.&lt;/p&gt;

&lt;p&gt;We’ll need a few more packages to get started so run this command in a&lt;br&gt;
separate terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add gatsby-transformer-remark rehype-react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Generating types and configuration
&lt;/h3&gt;

&lt;p&gt;This template uses a development tool to generate Typescript types from&lt;br&gt;
Graphql schemas. If this is all Greek to you that’s fine, I handle most&lt;br&gt;
of the setup for you. All you need to know is that we’ll need the types&lt;br&gt;
for the new transformer we added. But first, we need to do some&lt;br&gt;
configuration. In the codegen.yml file at the root of the project add&lt;br&gt;
this line under documents.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// codegen.yml  - node_modules/gatsby-transformer-remark/!(node_modules)/**/*.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will add the new types for Remark to our generated types file. This&lt;br&gt;
works fine for most uses but we need to extend the ‘frontmatter’ field&lt;br&gt;
to add some extra props such as slug. So open the typedefs.js file in&lt;br&gt;
src/graphql/typedefs.js to include these new types.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/grapql/typedefs.jstype

MarkdownRemarkFrontmatter {  
    author: AttributedUser
    title: String!
    slug: String!
    date: String
    featuredImage: String
}

type MarkdownRemark implements Node {  
    frontmatter: MarkdownRemarkFrontmatter
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last thing we need to do before generating types is update the&lt;br&gt;
gatsby-config with the plugin we added. So somewhere in the plugins&lt;br&gt;
array add this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// gatsby-config.js

plugins: [`gatsby-transformer-remark`]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then stop and restart your development process and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn generate-types
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;Gatsby templates with styled components&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Now we will need to tell Gatsby to generate the HTML files for our&lt;br&gt;
markdown. We’ll want control over how each of these pages looks but we&lt;br&gt;
also want them to all function the same. That’s where Gatsby templates&lt;br&gt;
come in.&lt;/p&gt;

&lt;p&gt;You can see an example of this in Gatsby’s docs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.gatsbyjs.com/docs/programmatically-create-pages-from-data/#specifying-a-template"&gt;&lt;strong&gt;Creating Pages from Data Programmatically&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’re going to create our own template and use it for layout and styling&lt;br&gt;
on our posts. In the src folder add a templates folder. And inside it&lt;br&gt;
add a styles folder with article-template.styled.tsx and index.ts files.&lt;br&gt;
Inside of the article-template.styled.tsx file add these styles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// templates/styles/article-template.styled.tsx

import styled from 'styled-components'

export const Article = styled.article`
  background-color: white;
  color: ${({ theme }) =&amp;gt; theme.colors.mediumGray};
  display: flex;
  flex-direction: column;
  padding: 2em 10vw 2em 10vw;`

export const Author = styled.div`
  display: flex;
  justify-content: center;`

export const AfterTitle = styled.div`
  margin: auto auto;
  width: 100%;

  @media all and (min-width: 700px) {
       width: 70%;
    }`

export const Content = styled.div`
   display: flex;
   flex: 1;
   margin-top: 2em;
   max-width: 100vw;
   overflow: hidden;
   word-wrap: break-word;

   div {
       max-width: 100%;
     }`

export const Heading = styled.h1`
  font-size: 2em;`

export const List = styled.ul`
  list-style-type: disc;`

export const Paragraph = styled.p`
  font-size: 1.2em;
  line-height: 1.5;`

export const SubHeading = styled.h2`
  font-size: 1.5em;`

export const Title = styled.h1`
  font-size: 3em;
  text-align: center;`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And export all the styles from the index.ts file like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// templates/styles/index.ts

export * from './article-template.styled'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, create an article-template.tsx file at the root of templates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/templates/article-template.tsx
import * as React from 'react'
import * as Styled from './styles'
import { Avatar, Image, Layout } from '../components'
import { ImageType } from '../enums'
import { Query } from '../interfaces'
import RehypeReact from 'rehype-react'
import { format } from 'date-fns'
import { graphql } from 'gatsby'

export const query = graphql`
    query($slug: String!) {
        allMarkdownRemark(filter: { frontmatter: { slug: { eq: $slug } } }) {
            edges {
                node {
                    frontmatter {
                        author {
                            avatar
                            name
                        }
                        date
                        featuredImage
                        title
                    }
                    excerpt
                    htmlAst
                }
            }
        }
    }
`

const articleTemplate: React.FC&amp;lt;{ data: { allMarkdownRemark: Query['allMarkdownRemark'] } }&amp;gt; = ({ data }) =&amp;gt; {
    if (!data) {
        return null
    }

    const {
        allMarkdownRemark: {
            edges: [
                {
                    node: { frontmatter, htmlAst }
                }
            ]
        }
    } = { ...data }

    const renderAst = new (RehypeReact as any)({
        components: { h1: Styled.Heading, h2: Styled.SubHeading, p: Styled.Paragraph, ul: Styled.List },
        createElement: React.createElement
    }).Compiler

    return (
        &amp;lt;Layout&amp;gt;
            {' '}
            &amp;lt;Styled.Article&amp;gt;
                {' '}
                {frontmatter &amp;amp;&amp;amp; (
                    &amp;lt;&amp;gt;
                        {' '}
                        &amp;lt;Styled.Title&amp;gt;{frontmatter.title}&amp;lt;/Styled.Title&amp;gt;{' '}
                        {frontmatter.author &amp;amp;&amp;amp; (
                            &amp;lt;Styled.Author&amp;gt;
                                {frontmatter.author.avatar &amp;amp;&amp;amp; &amp;lt;Avatar avatar={frontmatter.author.avatar} /&amp;gt;}{' '}
                                &amp;lt;Styled.SubHeading&amp;gt; {frontmatter.author.name} &amp;lt;/Styled.SubHeading&amp;gt;{' '}
                            &amp;lt;/Styled.Author&amp;gt;
                        )}{' '}
                        {(frontmatter.featuredImage || frontmatter.date) &amp;amp;&amp;amp; (
                            &amp;lt;Styled.AfterTitle&amp;gt;
                                {' '}
                                {frontmatter.featuredImage &amp;amp;&amp;amp; (
                                    &amp;lt;Image
                                        src={frontmatter.featuredImage}
                                        type={ImageType.FLUID}
                                        style={frontmatter.date ? { marginBottom: '10px' } : undefined}
                                    /&amp;gt;
                                )}{' '}
                                {frontmatter.date &amp;amp;&amp;amp; (
                                    &amp;lt;Styled.SubHeading style={{ textAlign: 'center' }}&amp;gt;
                                        {' '}
                                        {format(new Date(frontmatter.date), 'MMM do yyyy')}{' '}
                                    &amp;lt;/Styled.SubHeading&amp;gt;
                                )}{' '}
                            &amp;lt;/Styled.AfterTitle&amp;gt;
                        )}
                    &amp;lt;/&amp;gt;
                )}{' '}
                &amp;lt;Styled.Content&amp;gt;{renderAst(htmlAst)}&amp;lt;/Styled.Content&amp;gt;{' '}
            &amp;lt;/Styled.Article&amp;gt;{' '}
        &amp;lt;/Layout&amp;gt;
    )
}

export default articleTemplate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This may look complicated but all we’re doing is querying all the&lt;br&gt;
markdown and filtering it by the slug. The slug is used to determine the&lt;br&gt;
URL of the post and the front matter are fields like featured image and&lt;br&gt;
author. After we have the correct post we will render all the&lt;br&gt;
frontmatter I mentioned. Then use Rehype React to turn the raw HTML&lt;br&gt;
string into a component. Each of the defined basic HTML elements we&lt;br&gt;
specified get converted to styled-components. By doing so we have more&lt;br&gt;
control over the style of our posts.&lt;/p&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;Creating Pages as Blog Posts&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Here’s where the magic happens.&lt;/p&gt;

&lt;p&gt;We will be using the create pages hook provided by Gatsby to query our&lt;br&gt;
markdown into pages using the template we made. In the gatsby-config.js&lt;br&gt;
file add the hook.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// gatsby-config.js

exports.createPages = async ({ actions: { createPage }, graphql }) =&amp;gt; {
    const {
        data: { allMarkdownRemark, errors }
    } = await graphql(
        `
            {
                allMarkdownRemark {
                    edges {
                        node {
                            frontmatter {
                                slug
                            }
                        }
                    }
                }
            }
        `
    )

    if (!allMarkdownRemark || errors) {
        console.log('Error retrieving data', errors || 'No data could be found for this query!')
        return
    }

    const articleTemplate = require.resolve('./src/templates/article-template.tsx')

    allMarkdownRemark.edges.forEach((edge) =&amp;gt; {
        createPage({
            component: articleTemplate,
            context: { slug: edge.node.frontmatter.slug },
            path: `/blog/${edge.node.frontmatter.slug}/`
        })
    })
}

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

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;Navigating Posts&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We could just navigate manually to the URL in each of our posts but the&lt;br&gt;
user will need to be able to find and navigate to our posts. So first&lt;br&gt;
off create a blog folder in components and inside that folder create a&lt;br&gt;
post folder. From there create a styles folder and populate it with&lt;br&gt;
post.styled.tsx and index.ts files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// blog/post/styles/post.styled.tsx

import { Card } from '@material-ui/core'
import { Image } from '../../../image'
import { Link } from 'gatsby'
import styled from 'styled-components'

export const AuthorInfo = styled.div`
  align-items: center;
  display: flex;
  flex-direction: column;
  justify-content: center;

    h4 {
        margin: 0px;
   }`

export const FeaturedImage = styled(Image).attrs(() =&amp;gt; ({ 
  aspectRatio: 21 / 11,
  type: 'fluid'
}))`flex: 1;`

export const Info = styled.div`
  align-items: center;
  display: flex;
  flex-direction: column;
  margin-top: 1em;`

export const PostItem = styled(Card).attrs({ raised: true })`
  align-items: center;
  display: flex;
  flex-direction: column;
  text-align: center;`

export const PostContent = styled.span`padding: 1em;`

export const PostContentUpper = styled.div`
  margin-bottom: 10px; 

    h3 { 
        margin: 0px;
    }`

export const PostLink = styled(Link)`
  color: ${({ theme }) =&amp;gt; theme.colors.black};
  display: flex;
  flex: 1;
  flex-direction: column;
  text-decoration: none;
  width: 100%;`

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

&lt;/div&gt;



&lt;p&gt;Once again export the styles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// blog/post/styles/index.ts

export * from './post.styled'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s make the actual post component. We’ll need to pass along the&lt;br&gt;
‘frontmatter’ of each post in order to give the reader a taste of what&lt;br&gt;
the post is about.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// blog/post/post.tsx

import * as React from 'react'
import * as Styled from './styles'
import { MarkdownRemark, MarkdownRemarkFrontmatter } from '../../../interfaces'
import { Avatar } from '../../avatar'
import { CardProps } from '@material-ui/core'
import { GatsbyLinkProps } from 'gatsby'
import { format } from 'date-fns'

interface Post extends MarkdownRemarkFrontmatter, Omit&amp;lt;CardProps, 'title' | 'onClick'&amp;gt; {
    excerpt: MarkdownRemark['excerpt']
    onClick?: GatsbyLinkProps&amp;lt;Record&amp;lt;string, unknown&amp;gt;&amp;gt;['onClick']
}

const Post: React.FC&amp;lt;Post&amp;gt; = ({ author, className, date, excerpt, featuredImage, onClick, slug, title }) =&amp;gt; {
    return (
        &amp;lt;Styled.PostItem className={className}&amp;gt;
            &amp;lt;Styled.PostLink to={`/blog/${slug}`} onClick={onClick}&amp;gt;
                {' '}
                {featuredImage &amp;amp;&amp;amp; &amp;lt;Styled.FeaturedImage src={featuredImage} /&amp;gt;}{' '}
                &amp;lt;Styled.PostContent&amp;gt;
                    {' '}
                    &amp;lt;Styled.PostContentUpper&amp;gt;
                        {' '}
                        &amp;lt;h3&amp;gt;{title}&amp;lt;/h3&amp;gt;
                        &amp;lt;Styled.Info&amp;gt;
                            {author &amp;amp;&amp;amp; (
                                &amp;lt;Styled.AuthorInfo&amp;gt;
                                    {' '}
                                    {author.avatar &amp;amp;&amp;amp; &amp;lt;Avatar avatar={author.avatar} /&amp;gt;} &amp;lt;h4&amp;gt;{author.name}&amp;lt;/h4&amp;gt;{' '}
                                &amp;lt;/Styled.AuthorInfo&amp;gt;
                            )}{' '}
                            {date &amp;amp;&amp;amp; &amp;lt;h5&amp;gt;{format(new Date(date), 'MMM do yyyy')}&amp;lt;/h5&amp;gt;}{' '}
                        &amp;lt;/Styled.Info&amp;gt;{' '}
                    &amp;lt;/Styled.PostContentUpper&amp;gt;
                    &amp;lt;p&amp;gt;{excerpt}&amp;lt;/p&amp;gt;
                &amp;lt;/Styled.PostContent&amp;gt;{' '}
            &amp;lt;/Styled.PostLink&amp;gt;
        &amp;lt;/Styled.PostItem&amp;gt;
    )
}

export default Post

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

&lt;/div&gt;



&lt;p&gt;We might want to use this component in other places on our site so go&lt;br&gt;
ahead and export it from the root of the post folder with another&lt;br&gt;
index.ts file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// blog/post/index.ts

export { default as Post } from './post'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll need a component to display our yummy posts in, so go ahead and&lt;br&gt;
make a styles folder at the root of components/blog. Just like the post&lt;br&gt;
example, you’ll create a blog.styled.tsx file and an index.ts file&lt;br&gt;
inside the styles folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// blog/styles/blog.styled.tsx

import styled from 'styled-components'

export const Blog = styled.div`
  align-items: center;
  background-color: ${({ theme }) =&amp;gt; theme.colors.white};
  display: flex;   justify-content: center;
  min-height: 100vh;
  padding: 1em 0 1em 0;`

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

&lt;/div&gt;



&lt;p&gt;And don’t forget to export:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// blog/styles/index.ts

export * from './blog.styled'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If our posts are peanut butter inside the sandwich of the blog page then&lt;br&gt;
the blog component is the jelly. It uses a grid component I provided to&lt;br&gt;
hold posts together in a simple but effective manner on the page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// blog/blog.tsx

import * as React from 'react'
import * as Styled from './styles'
import { MarkdownRemark, MarkdownRemarkFrontmatter } from '../../interfaces'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { Grid } from '../grid'
import { Post } from './post'
import { faBlog } from '@fortawesome/free-solid-svg-icons'

interface BlogProps {
    posts: MarkdownRemark[]
}

const Blog: React.FC&amp;lt;BlogProps&amp;gt; = ({ posts }) =&amp;gt; {
    const blogItems = React.useMemo(() =&amp;gt; {
        const postsWithFrontMatter = posts.filter(({ frontmatter }) =&amp;gt; frontmatter)

        if (postsWithFrontMatter.length &amp;lt;= 0) {
            return null
        }

        return postsWithFrontMatter.map(({ frontmatter, excerpt, id }) =&amp;gt; (
            &amp;lt;Post key={id} {...(frontmatter as MarkdownRemarkFrontmatter)} excerpt={excerpt} /&amp;gt;
        ))
    }, [posts])

    return (
        &amp;lt;Styled.Blog&amp;gt;
            {' '}
            {blogItems ? (
                &amp;lt;Grid items={blogItems} style={{ width: '90%' }} /&amp;gt;
            ) : (
                &amp;lt;h2&amp;gt;
                    No blog posts yet but check back soon!&amp;amp;nbsp; &amp;lt;FontAwesomeIcon icon={faBlog} /&amp;gt;
                &amp;lt;/h2&amp;gt;
            )}{' '}
        &amp;lt;/Styled.Blog&amp;gt;
    )
}

export default Blog

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

&lt;/div&gt;



&lt;p&gt;And this is the final time I’ll have you export something from another&lt;br&gt;
file I promise. In the index.ts file at the root of the components&lt;br&gt;
folder add this line at the top.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// components/index.ts

export * from './blog'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you took a look at the demo I gave earlier for this template you’ll&lt;br&gt;
have noticed that the latest post section included a familiar article.&lt;br&gt;
In this tutorial, I won’t go into creating this latest post section on&lt;br&gt;
but I will have you export the Blog and Post components so they can be&lt;br&gt;
used elsewhere.&lt;/p&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;Putting it all together&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Now we’re done with the hard part. We have the pieces needed for&lt;br&gt;
displaying our brilliant posts all that’s left is to create the page to&lt;br&gt;
display them and at least one sample post to try it out. Find the pages&lt;br&gt;
folder at src/pages and add a blog.tsx file. This will be the page that&lt;br&gt;
displays our blog component and posts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/pages/blog.tsx

import * as React from 'react'
import { Blog, Layout, SEO } from '../components'
import { Query } from '../interfaces'
import { graphql } from 'gatsby'

export const query = graphql`
    query {
        allMarkdownRemark {
            totalCount
            edges {
                node {
                    id
                    frontmatter {
                        author {
                            avatar
                            name
                        }
                        slug
                        title
                        date
                        featuredImage
                    }
                    excerpt
                }
            }
        }
    }
`

const BlogPage: React.FC&amp;lt;{ data: { allMarkdownRemark: Query['allMarkdownRemark'] } }&amp;gt; = ({
    data: {
        allMarkdownRemark: { edges }
    }
}) =&amp;gt; {
    return (
        &amp;lt;Layout&amp;gt;
            {' '}
            &amp;lt;SEO title="Blog" /&amp;gt; &amp;lt;Blog posts={edges.map(({ node }) =&amp;gt; node)} /&amp;gt;
        &amp;lt;/Layout&amp;gt;
    )
}

export default BlogPage

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

&lt;/div&gt;



&lt;p&gt;This page will look for all of our markdown files and pass them along to&lt;br&gt;
the blog component as posts. If you go to&lt;br&gt;
&lt;a href="http://localhost:8001/blog"&gt;localhost:8001/blog&lt;/a&gt; you should see an&lt;br&gt;
empty blog page with a no posts message.&lt;/p&gt;

&lt;p&gt;Now is the moment of truth, we need to make a sample post to make sure&lt;br&gt;
this is all working. Go ahead and create a folder in src/content called&lt;br&gt;
posts and inside it create a what-time-is-it.md file. We’ll be using the&lt;br&gt;
lyrics to ‘Peanut Butter Jelly Time’ as a fitting test.&lt;br&gt;
&lt;/p&gt;

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

author: { avatar: 'bannans.png', name: 'Buckwheat Boyz' }
title: 'What time is it?'
slug: 'what-time-is-it'
date: '2/1/2021'
---

It's peanut butter jelly time!
Peanut butter jelly time!
Peanut butter jelly time!

&amp;lt;!-- endexcerpt --&amp;gt;

Now Where he at?
Where he at?
Where he at?
Where he at?

NowThere he go
There he go
There he go
There he go

## Peanut butter jelly [x4]

Do the Peanut butter jelly
Peanut butter jelly
Peanut butter jelly with a baseball bat

Do the Peanut butter jelly
Peanut butter jelly
Peanut butter jelly with a baseball bat

## Chorus

Now break it down and freeze
Take it down to your knees
Now lean back and squeeze
Now get back up and scream

## Chorus

Now sissy walk
Sissy walk
Sissy walk
Sissy walk

Now sissy walk
Sissy walk
Sissy walk
Sissy walk

## Chorus

Now walk walk walk walk
Stomp stomp stomp stomp
Slide slide slide slide
Back it up one more time

Now walk walk walk walk
Stomp stomp stomp stomp

Peanut butter jelly break it down
Throw the ball up swing that bat

Turn your head back and see where it at
Throw the ball up swing that bat
Turn you head back and see where it at

Palm beachpeanut butter
Dade countyjelly
Orlandopeanut butter
Tallahasse jelly

Hold on hold on hold on hold on
"Hey chip man what time is it?"

"I don't know what time it is ray low"

"It's peanut butter jelly time"

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

&lt;/div&gt;



&lt;p&gt;You should see our what-time-is-it blog post appear on the blog page and&lt;br&gt;
clicking it will, in fact, tell you what time it is.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;You should now understand the concepts behind querying markdown files&lt;br&gt;
and changing them into HMTL pages. To recap, we added and generated&lt;br&gt;
types for the Remark transformer in Gatsby. Then we made a template to&lt;br&gt;
use for our markdown that converts each file into valid HTML with&lt;br&gt;
styles. We then set up a create pages hook that uses a template to&lt;br&gt;
render our posts. And finally, we made a page with blog and post&lt;br&gt;
components to display those posts for site visitors to enjoy.&lt;/p&gt;

&lt;p&gt;I hope you enjoyed this tutorial and learned a few things along the way.&lt;br&gt;
This is my first attempt at creating a Gatsby website template and would&lt;br&gt;
love feedback.&lt;/p&gt;

&lt;p&gt;If you got lost or didn’t have the time to follow along you can get the&lt;br&gt;
$5 version of the template at the link I listed at the beginning of&lt;br&gt;
this tutorial. It includes all the code I went over here as&lt;br&gt;
well as a few more features such as the latest post section.&lt;/p&gt;

&lt;p&gt;But most importantly, what’s the best kind of peanut butter; crunchy or&lt;br&gt;
smooth? Let the debate ensue in the comments section, thanks!&lt;/p&gt;

&lt;p&gt;By &lt;a href="https://medium.com/@john-grisham"&gt;John Grisham&lt;/a&gt; on &lt;a href="https://medium.com/p/80636d6ca8e9"&gt;February 2,&lt;br&gt;
2021&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>gatsby</category>
      <category>javascript</category>
      <category>react</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Using CRUD operations with React SWR for mutating REST API cache</title>
      <dc:creator>John Grisham</dc:creator>
      <pubDate>Wed, 02 Dec 2020 23:17:15 +0000</pubDate>
      <link>https://dev.to/squashbugler/using-crud-operations-with-react-swr-for-mutating-rest-api-cache-54kg</link>
      <guid>https://dev.to/squashbugler/using-crud-operations-with-react-swr-for-mutating-rest-api-cache-54kg</guid>
      <description>&lt;p&gt;To support me please read this tutorial at its original posting location on Medium:&lt;br&gt;
&lt;a href="https://levelup.gitconnected.com/using-crud-operations-with-react-swr-for-mutating-rest-api-cache-3e0d01774aed"&gt;Using CRUD operations with React SWR for mutating REST API cache&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;SWR for making fetch requests&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Vercel has made some great libraries and frameworks in the past so it's no surprise that the SWR library would be any different. I'm going to show you how to fetch and manipulate data from a REST API with Vercel's SWR library. This post has a quick overview of the Vercel library, but if you want to learn more about the library and how it works, you can read the full documentation here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://swr.vercel.app/"&gt;SWR: React Hooks for Data Fetching&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is SWR?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The idea behind SWR which stands for stale while revalidating is defined in the docs as such. SWR is a strategy to first return the data from cache (stale), then send the fetch request (revalidate), and finally, come with the up-to-date data. So what does this have to do with CRUD? In case you didn't know CRUD is a set of operations that are performed on data and it is shorthand for create, read, update and delete. By default, SWR will perform the read part of this for you by returning the result of a fetch request. But if you want to expand this you will have to mutate the cache from that request. That's why I created a useCrud hook that will help us do just that. I also incorporate Typescript to ensure that the proper keys are used when updating the cache so you will need to have that set up as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Setting things up&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;So first thing is to install SWR, to do this run:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install swr
or
yarn add swr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will add the SWR library to your project. Next, we will add a configuration provider for our app. This will provide the global configuration for SWR when we make requests. I have a contexts folder where I store contexts like this.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import * as React from 'react'
import { SWRConfig } from 'swr'

const swrConfig = {
 revalidateOnFocus: false,
 shouldRetryOnError: false
}

export const SWRConfigurationProvider: React.FC = ({ children }) =&amp;gt; &amp;lt;SWRConfig value={swrConfig}&amp;gt;{children}&amp;lt;/SWRConfig&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will need to wrap around your app root, for me that is in the pages/_app.tsx file because I'm using NextJS but it can work in another framework like Gatsby as long as it wraps your app globally. Feel free to change the settings as needed for your project.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;R you ready to read some data?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Now we will need to start implementing the fetch that will form the basis of the hook. Here is an example of how fetching works in SWR.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const fetcher = useCallback(
 async (url: string) =&amp;gt; {
 const response = await fetch(url)
 return response as T[]
 },
 []
 )

const { data, error, isValidating, mutate } = useSWR(url, fetcher, {
 fetchOptions
 })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The useSWR hook is pretty straight forward it takes a URL and a 'fetcher' which is the function that will perform the request. The URL is passed to the fetcher to make the request and you can also provide some nifty options. SWR will return some things back for you the first is the data that was returned, an error status if there is one, a mutate function, and an isValidating boolean that will tell you if the data is fresh or not. You can think of the isValidating flag as a loading indicator; it isn't quite the same thing but for my purposes it is.&lt;/p&gt;

&lt;p&gt;Go ahead and create a use-crud.tsx file wherever you put your custom hooks and add this to start.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import useSWR, { ConfigInterface } from 'swr'
import { useCallback } from 'react'

// T is the response type
// K is the request type which defaults to T
export function useCrud&amp;lt;T, K = T&amp;gt;(url: string, key: keyof T, fetchOptions?: ConfigInterface) {
 const fetch = useCallback(
 async (url: string) =&amp;gt; {
 const response = await fetch(url)
 return response as T[]
 },
 []
 )

const { data, error, isValidating, mutate } = useSWR(url, fetch, {
 fetchOptions
 })

return {
 fetch: {
 data,
 error,
 loading: isValidating,
 mutate
 }
 }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Making it user friendly
&lt;/h2&gt;

&lt;p&gt;I'll go over the parameters and types later but for now all you need to know is that we will be able to pass a URL to this hook and it will give us the data and the methods to perform CRUD operations on that data. There's just one problem that I ran into. Sometimes the response is too quick for my app since we have the cached data to fall back on so I added a loading state and timeout to make the request take at least half a second. This will improve the user experience.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useCallback, useEffect, useState } from 'react'
import useSWR, { ConfigInterface } from 'swr'

// T is the response type
// K is the request type which defaults to T
export function useCrud&amp;lt;T, K = T&amp;gt;(url: string, key: keyof T, fetchOptions?: ConfigInterface) {
const [loading, setIsLoading] = useState(true)

const loadingTimeout = () =&amp;gt; {
 setIsLoading(false)
 }

const fetch = useCallback(
 async (url: string) =&amp;gt; {
 const response = await fetch(url)
 return response as T[]
 },
 []
 )

const { data, error, isValidating, mutate } = useSWR(url, fetch, {
 fetchOptions
 })

useEffect(() =&amp;gt; {
 if (isValidating) {
 setIsLoading(true)
 return
 }

setTimeout(loadingTimeout, 500)
 }, [isValidating])

return {
 fetch: {
 data,
 error,
 loading,
 mutate
 }
 }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;There's one little quirk with SWR that I need to mention. When there is no data from a request an empty object is returned; that's not really what I want so I added an extra step to check if the data is empty. For that I will use lodash, go ahead and install it if you haven't already. If the object is empty I will return an empty array instead, update your imports to add this.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { isArray, isEmpty } from 'lodash'
import { useCallback, useEffect, useMemo, useState } from 'react'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We'll need the isArray method later for the CRUD operations and we'll be memoizing the result of the data check. Add this above the return statement.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const memoizedData = useMemo(() =&amp;gt; (!isEmpty(data) ? data : []), [data])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And then return memoizedData instead of data.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;return {
 fetch: {
 data: memoizedData,
 error,
 loading,
 mutate
 }
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;C what I did there&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Now the moment you've been waiting for, we're going to start modifying the data but before we do that let me explain the Typescript parameters of this function. The T generic type is the type of data we'll be expecting to get back and the K generic type is the type of data we will be using to perform the create operation. In most cases, this will be the same but in case we need to perform some operations on that data before sending it we will use a different type. As you can see it defaults to T anyway if we don't pass anything. The key in the parameters is a key of the type T which means any props on the type can be used but we need to tell typescript what the index key is so we can mutate the cached data from the fetch. The create operation will look like this.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const create = useCallback(
 async (newObject: K, shouldRevalidate = false) =&amp;gt; {
 const response = await fetch(url, {
 body: newObject,
 method: 'POST'
 })

const result = response as T

if (data &amp;amp;&amp;amp; mutate) {
 let newData = data
 if (isArray(data)) {
 newData = data.concat(result)
 }

await mutate([new Set(newData)], shouldRevalidate)
 }

return result
 },
 [url, data, mutate]
 )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Two is better than one&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This will create a new object in our URL post method. If we have data it will mutate its cache if we don't we'll just return the result of the post. There is an additional check to see if the data is an array, if it is we will add the new object to the data array if it isn't we will add a new set of data and skip revalidation. I went ahead and added a parameter for revalidation that can be overridden if we want the new data and not just the cache. This will call the mutate function we got earlier and allow us to mutate the cache with the new data and return an optimistic response of what the new array should look like; all without fetching the data again. But this method will only work for creating a single instance so we will need one for creating multiple objects as well.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const createMultiple = useCallback(
 async (newObjects: K[], shouldRevalidate = false) =&amp;gt; {
 const response = await fetch(url, {
 body: newObjects,
 method: 'POST'
 })

const result = response as T[]

if (data &amp;amp;&amp;amp; mutate) {
 await mutate([data, result], shouldRevalidate)
 }

return result
 },
 [url, data, mutate]
 )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Gimme the D
&lt;/h2&gt;

&lt;p&gt;This separate method will handle creating more than one object. One improvement would be to combine these but this will work for the purpose of the tutorial. Next, we'll handle the removal operation of CRUD. The function should look like this.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const remove = useCallback(
 async (body: number, shouldRevalidate = false) =&amp;gt; {
 const response = await fetch(url, {
 body,
 method: 'DELETE'
 })
 const result = response as T

if (data &amp;amp;&amp;amp; mutate) {
 if (isArray(result)) {
 const updatedObjects = [data].filter((current) =&amp;gt; {
 const isDeleted = result.find((result) =&amp;gt; result[key] === current[key])
 return !isDeleted
 })

 await mutate(result.length === 0 ? [] : updatedObjects, shouldRevalidate)
 } else {
 const deletedIndex = data.findIndex((object) =&amp;gt; object[key] === result[key])

if (deletedIndex &amp;gt;= 0) {
 const updatedObjects = [data]
 updatedObjects.splice(deletedIndex, 1)

        await mutate(updatedObjects, shouldRevalidate)
       }
    }
 }

return result
 },
 [url, data, key, mutate]
 )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will take a number for the key you are modifying so you can get that from the data you got from the original fetch and parse it according to whichever item you are removing. If the result of this operation is an array then we will find each item in the data that matches the key and remove it from the list. Otherwise, we will have to find the index of the object that was deleted and if it is in the list remove that index. One important note is that each of these requests should return the value of whatever object was manipulated so that we can update the cache. Removing multiple objects is very similar.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const removeMultiple = useCallback(
 async (ids: number[], shouldRevalidate = false) =&amp;gt; {
 const response = await fetch(url, {
 body: ids,
 method: 'DELETE'
 })
 const results = response as T[]

if (data &amp;amp;&amp;amp; mutate) {
 const updatedObjects = [data].filter((current) =&amp;gt; {
 const isDeleted = results.find((result) =&amp;gt; result[key] === current[key])
 return !isDeleted
 })

        await mutate(updatedObjects, shouldRevalidate)

        return results
       }
   },
 [url, data, key, mutate]
 )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;U know what comes next&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The update part of CRUD is a little different since the SQL server can throw an error if the rows being updated aren't different. For this, you should probably have some validation on the front end to make sure that doesn't happen but just in case I will make a check for it here using a method I stole. Create a helper method called get-object-difference.ts somewhere you can easily access it.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { isEqual } from 'lodash'

/*
 * Compare two objects by reducing an array of keys in obj1, having the
 * keys in obj2 as the initial value of the result. Key points:
 *
 * ' All keys of obj2 are initially in the result.
 *
 * ' If the loop finds a key (from obj1, remember) not in obj2, it adds
 * it to the result.
 *
 * ' If the loop finds a key that is both in obj1 and obj2, it compares
 * the value. If it's the same value, the key is removed from the result.
 */
export function getObjectDifference(obj1: any, obj2: any) {
 const diff = Object.keys(obj1).reduce((result, key) =&amp;gt; {
 if (!obj2.hasOwnProperty(key)) {
 result.push(key)
 }
 return result
 }, Object.keys(obj2))

return Object.fromEntries(
 diff.map((key) =&amp;gt; {
 return [key, obj2[key]]
 })
 )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This method will return an object of the difference between two objects otherwise it will return an empty object if there is none. Go ahead and import it into the useCrud file and add the update method.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const update = useCallback(
 async (updatedObject: T, shouldRevalidate = false): Promise&amp;lt;T&amp;gt; =&amp;gt; {
 const currentObjectIndex = data.findIndex((object) =&amp;gt; object[key] === updatedObject[key])
 const currentObject = data[currentObjectIndex]
 const diff = currentObject ? getObjectDifference(currentObject, updatedObject) : null

if (!diff) {
 throw new Error('Update Failed')
 }

if (isEmpty(diff)) {
 return currentObject
 }

const response = await fetch(url, {
 body: { diff, id: updatedObject[key] },
 method: 'PATCH'
 })

if (data &amp;amp;&amp;amp; mutate) {
 const updatedObjects = [data]
 updatedObjects.splice(currentObjectIndex, 1, response)
 await mutate(updatedObjects, shouldRevalidate)
 }

return response as T
 },
 [url, data, mutate, key]
 )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will check the cache for the current object you are modifying and get the difference between the old object and the new one. If the current object doesn't exist in the cache it will throw an error. Otherwise, if there is no difference it will just return the current object and not execute the fetch request to patch. If there is a difference it will pass the difference and the updated object's id as whatever key you specified earlier on the updated object. It will then go ahead and perform the mutate on the cached data, updating multiple objects is slightly different.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const updateMultiple = useCallback(
 async (updatedObjects: T[], shouldRevalidate = false): Promise&amp;lt;T[]&amp;gt; =&amp;gt; {
 const currentObjects = data.filter((object) =&amp;gt; updatedObjects.find((updated) =&amp;gt; object[key] === updated[key]))

if (!currentObjects || currentObjects &amp;lt;= 0) {
 throw new Error('Update Failed')
 }

const diffs = currentObjects.map((currentObject) =&amp;gt; {
 const updatedObject = updatedObjects.find((updated) =&amp;gt; updated[key] === currentObject[key])
 return { getObjectDifference(currentObject, updatedObject), id: updatedObject[key] }
 })

if (diffs.length &amp;lt;= 0) {
 return currentObjects
 }

const response = await fetch(url, {
 body: { diffs },
 method: 'PATCH'
 })

if (data &amp;amp;&amp;amp; mutate) {
 const updatedObjects = [data].map((current) =&amp;gt; {
 if (current[key] === response[key]) {
 return response
 }

   return current
 })

   await mutate(updatedObjects, shouldRevalidate)
 }

return response as T[]
 },
 [url, data, mutate, key]
 )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will run the difference check on all the objects and instead pass an array of object differences in the body. All of these implementations are of course specific to my API routes but they could easily be modified to work with your use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up this spelling lesson
&lt;/h2&gt;

&lt;p&gt;Phew! If you made it this far I owe you a drink but since I can't buy you one right now instead I'll give you the full code.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { isArray, isEmpty } from 'lodash'&lt;br&gt;
import { useCallback, useEffect, useMemo, useState } from 'react'&lt;br&gt;
import useSWR, { ConfigInterface } from 'swr'&lt;br&gt;
import { getObjectDifference } from '../where-ever-you-put-this-earlier'

&lt;p&gt;// T is the response type&lt;br&gt;
// K is the request type which defaults to T&lt;br&gt;
export function useCrud&amp;lt;T, K = T&amp;gt;(url: string, key: keyof T, fetchOptions?: ConfigInterface) {&lt;br&gt;
const [loading, setIsLoading] = useState(true)&lt;/p&gt;

&lt;p&gt;const loadingTimeout = () =&amp;gt; {&lt;br&gt;
setIsLoading(false)&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;const fetch = useCallback(&lt;br&gt;
async (url: string) =&amp;gt; {&lt;br&gt;
const response = await fetch(url)&lt;br&gt;
return response as T[]&lt;br&gt;
},[])&lt;/p&gt;

&lt;p&gt;const { data, error, isValidating, mutate } = useSWR(url, fetch, {fetchOptions})&lt;/p&gt;

&lt;p&gt;useEffect(() =&amp;gt; {&lt;br&gt;
if (isValidating) {&lt;br&gt;
setIsLoading(true)&lt;br&gt;
return&lt;br&gt;
}setTimeout(loadingTimeout, 500)},&lt;br&gt;
[isValidating])&lt;/p&gt;

&lt;p&gt;const create = useCallback(&lt;br&gt;
async (newObject: K, shouldRevalidate = false) =&amp;gt; {&lt;br&gt;
const response = await fetch(url, {&lt;br&gt;
body: newObject,&lt;br&gt;
method: 'POST'&lt;br&gt;
})&lt;/p&gt;

&lt;p&gt;const result = response as T&lt;br&gt;
if (data &amp;amp;&amp;amp; mutate) {&lt;br&gt;
let newData = data&lt;br&gt;
if (isArray(data)) {&lt;br&gt;
newData = data.concat(result)&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;await mutate([new Set(newData)], shouldRevalidate)&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;return result&lt;br&gt;
},[url, data, mutate])&lt;/p&gt;

&lt;p&gt;const createMultiple = useCallback(async (newObjects: K[], shouldRevalidate = false) =&amp;gt; {&lt;br&gt;
const response = await fetch(url, {&lt;br&gt;
body: newObjects,&lt;br&gt;
method: 'POST'&lt;br&gt;
})&lt;/p&gt;

&lt;p&gt;const result = response as T[]&lt;br&gt;
if (data &amp;amp;&amp;amp; mutate) {&lt;br&gt;
await mutate([data, result], shouldRevalidate)}&lt;br&gt;
return result&lt;br&gt;
},[url, data, mutate])&lt;/p&gt;

&lt;p&gt;const remove = useCallback(async (body: number | unknown, shouldRevalidate = false) =&amp;gt; {&lt;br&gt;
const response = await fetch(url, {&lt;br&gt;
body,&lt;br&gt;
method: 'DELETE'&lt;br&gt;
})&lt;/p&gt;

&lt;p&gt;const result = response as T&lt;br&gt;
if (data &amp;amp;&amp;amp; mutate) {&lt;br&gt;
if (isArray(result)) {&lt;br&gt;
const updatedObjects = [data].filter((current) =&amp;gt; {&lt;br&gt;
const isDeleted = result.find((result) =&amp;gt; result[key] === current[key])&lt;br&gt;
return !isDeleted&lt;br&gt;
})&lt;/p&gt;

&lt;p&gt;await mutate(result.length === 0 ? [] : updatedObjects, shouldRevalidate)&lt;br&gt;
} else {&lt;/p&gt;

&lt;p&gt;const deletedIndex = data.findIndex((object) =&amp;gt; object[key] === result[key])&lt;br&gt;
if (deletedIndex &amp;gt;= 0) {&lt;br&gt;
const updatedObjects = [data]&lt;br&gt;
updatedObjects.splice(deletedIndex, 1)&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    await mutate(updatedObjects, shouldRevalidate)
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;}&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;return result&lt;br&gt;
},[url, data, key, mutate])&lt;/p&gt;

&lt;p&gt;const removeMultiple = useCallback(async (ids: number[], shouldRevalidate = false) =&amp;gt; {&lt;br&gt;
const response = await fetch(url, {&lt;br&gt;
body: ids,&lt;br&gt;
method: 'DELETE'&lt;br&gt;
})&lt;/p&gt;

&lt;p&gt;const results = response as T[]&lt;br&gt;
if (data &amp;amp;&amp;amp; mutate) {&lt;br&gt;
const updatedObjects = [data].filter((current) =&amp;gt; {&lt;br&gt;
const isDeleted = results.find((result) =&amp;gt; result[key] === current[key])&lt;br&gt;
return !isDeleted&lt;br&gt;
})&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; await mutate(updatedObjects, shouldRevalidate)

 return results
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;}&lt;br&gt;
},&lt;br&gt;
[url, data, key, mutate])&lt;/p&gt;

&lt;p&gt;const update = useCallback(async (updatedObject: T, shouldRevalidate = false): Promise&amp;lt;T&amp;gt; =&amp;gt; {&lt;/p&gt;

&lt;p&gt;const currentObjectIndex = data.findIndex((object) =&amp;gt; object[key] === updatedObject[key])&lt;/p&gt;

&lt;p&gt;const currentObject = data[currentObjectIndex]&lt;br&gt;
const diff = currentObject ? getObjectDifference(currentObject, updatedObject) : null&lt;/p&gt;

&lt;p&gt;if (!diff) {&lt;br&gt;
   throw new Error('Update Failed')&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;if (isEmpty(diff)) {&lt;br&gt;
   return currentObject&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;const response = await fetch(url, {&lt;br&gt;
body: { diff, id: updatedObject[key] },&lt;br&gt;
method: 'PATCH'&lt;br&gt;
})&lt;br&gt;
if (data &amp;amp;&amp;amp; mutate) {&lt;br&gt;
const updatedObjects = [data]&lt;br&gt;
updatedObjects.splice(currentObjectIndex, 1, response)&lt;br&gt;
  await mutate(updatedObjects, shouldRevalidate)&lt;br&gt;
}&lt;br&gt;
return response as T&lt;br&gt;
},[url, data, mutate, key])&lt;/p&gt;

&lt;p&gt;const updateMultiple = useCallback(async (updatedObjects: T[], shouldRevalidate = false): Promise&amp;lt;T[]&amp;gt; =&amp;gt; {&lt;br&gt;
const currentObjects = data.filter((object) =&amp;gt; updatedObjects.find((updated) =&amp;gt; object[key] === updated[key]))&lt;/p&gt;

&lt;p&gt;if (!currentObjects || currentObjects &amp;lt;= 0) {&lt;br&gt;
  throw new Error('Update Failed')&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;const diffs = currentObjects.map((currentObject) =&amp;gt; {&lt;br&gt;
  const updatedObject = updatedObjects.find((updated) =&amp;gt; updated[key] === currentObject[key])&lt;/p&gt;

&lt;p&gt;return { getObjectDifference(currentObject, updatedObject), id:   updatedObject[key] }&lt;br&gt;
})&lt;/p&gt;

&lt;p&gt;if (diffs.length &amp;lt;= 0) {&lt;br&gt;
  return currentObjects&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;const response = await fetch(url, {&lt;br&gt;
body: { diffs },&lt;br&gt;
method: 'PATCH'&lt;br&gt;
})&lt;/p&gt;

&lt;p&gt;if (data &amp;amp;&amp;amp; mutate) {&lt;br&gt;
  const updatedObjects = [data].map((current) =&amp;gt; {&lt;br&gt;
   if (current[key] === response[key]) {&lt;br&gt;
  return response&lt;br&gt;
}&lt;br&gt;
  return current&lt;br&gt;
})&lt;/p&gt;

&lt;p&gt;await mutate(updatedObjects, shouldRevalidate)&lt;br&gt;
}&lt;br&gt;
  return response as T[]&lt;br&gt;
},[url, data, mutate, key])&lt;/p&gt;

&lt;p&gt;const memoizedData = useMemo(() =&amp;gt; (!isEmpty(data) ? filterDeleted&amp;lt;T&amp;gt;(data) : []), [data])&lt;/p&gt;

&lt;p&gt;return {&lt;br&gt;
    create,&lt;br&gt;
    createMultiple,&lt;br&gt;
    fetch: { data: memoizedData, error, loading, mutate },&lt;br&gt;
    remove,&lt;br&gt;
    removeMultiple,&lt;br&gt;
    update,&lt;br&gt;
    updateMultiple&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Congratulations you've made it through this tutorial, this hook should give you all the functionality you need to perform CRUD operations with a custom restful API. This implementation is specific to my API so you may have to modify it for your use purposes but it is generic enough to be used in most cases. Thanks for joining me, I hope you enjoyed this load of CRUD.&lt;/p&gt;

&lt;p&gt;Please follow me on Twitter: &lt;a href="https://twitter.com/SquashBugler"&gt;@SquashBugler&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>api</category>
    </item>
    <item>
      <title>You're doing Javascript exports wrong, here's why.</title>
      <dc:creator>John Grisham</dc:creator>
      <pubDate>Fri, 13 Nov 2020 16:09:16 +0000</pubDate>
      <link>https://dev.to/squashbugler/you-re-doing-javascript-exports-wrong-here-s-why-1bpm</link>
      <guid>https://dev.to/squashbugler/you-re-doing-javascript-exports-wrong-here-s-why-1bpm</guid>
      <description>&lt;p&gt;To support me please read this tutorial at its original posting location on Medium:&lt;br&gt;
&lt;a href="https://medium.com/javascript-in-plain-english/heres-why-you-re-doing-javascript-exports-wrong-10072c96acea"&gt;You're doing Javascript exports wrong, here's why.&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;A lot of starting developers wonder what is the best way to export something. Maybe for you it's just about your personal preference and what you're used to but surely there must be some sort of best practice right? Should you use default exports or named? Should you export from the file itself or an index? The answer to these questions is yes.&lt;/p&gt;

&lt;p&gt;I'm going to make a bold statement here. The code you write isn't for you. (Unless you're the type that enjoys being a hermit and never working with others). Before I get into why this matters here's an explanation of each flavor of export.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Default exports&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Exporting: export default thing&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Importing: import thingy from 'place&lt;/strong&gt;'&lt;/p&gt;

&lt;p&gt;This is pretty basic you have one thing that you want to export and it doesn't matter what it's called only that it's the main thing being exported. Note that above although I exported the package as 'thing' I am importing it as 'thingy'. That's because default imports don't care about names only that they are the default package. These are great for namespaces or roots of libraries where aliasing is ok.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Named exports&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Exporting: export const thing / export { thing }&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Importing: import { thing } from 'place'&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The main difference here is the brackets. We denote a named import with these and as the name implies it has to be called the same Importing it as we exported it. I personally prefer named exports because we are forced into using consistent names this way and avoids confusion.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;File exports&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Importing: import Bob from './street/bobs-house'&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There is no export example here because it doesn't matter how we export it but where we export it from. In this example we would export from the file where our code exists. Think of a house where someone lives. If I want to find Bob I go to Bob's house directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Indexed exports&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Exporting: export { Bob } from './bobs-house'&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Importing: import { Bob } from './street'&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What if I don't know where Bob lives but I do know the street. Well this is where an index would come in handy. Think of an index as a phone book (wow did I just show my age) or Google maps. It's basically a way to direct us from the street in this example to the house.&lt;/p&gt;

&lt;p&gt;So in your street folder you would have two files, bobs-house and the index file. When you import from a folder without specifying a file JavaScript will import the index file by default. This can be great when you have a bunch of individual files that you want to import from on the same line.&lt;/p&gt;

&lt;p&gt;So why should you care about other developers when you export? Well think of it like this Imagine that your code is a dinner party. And your teammates or party guests are attending. In this situation you would want to be a good host correct? And since different developers prefer importing in different ways we should try to be as accommodating as possible.&lt;/p&gt;

&lt;p&gt;That means putting in the extra work to export our files as many ways as possible. So they can have a main course of default import and a side of named too. Thanks for joining me, I hope I was a good host!&lt;/p&gt;

&lt;p&gt;Follow me on Twitter to get this article and others: @&lt;a href="https://twitter.com/SquashBugler"&gt;SquashBugler&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>beginners</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Adding Firebase Authentication in Gatsby With a Little Typescript Magic</title>
      <dc:creator>John Grisham</dc:creator>
      <pubDate>Tue, 27 Oct 2020 13:26:07 +0000</pubDate>
      <link>https://dev.to/squashbugler/adding-firebase-authentication-in-gatsby-with-a-little-typescript-magic-202</link>
      <guid>https://dev.to/squashbugler/adding-firebase-authentication-in-gatsby-with-a-little-typescript-magic-202</guid>
      <description>&lt;p&gt;To support me please read this tutorial at its original posting location on Medium:&lt;br&gt;
&lt;a href="https://medium.com/swlh/adding-firebase-authentication-in-gatsby-with-a-little-typescript-magic-adf6ad1fbfb2"&gt;Setup Gatsby Firebase Authentication with Typescript in 10 minutes&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;br&gt;
    Gatsby is a great framework for building and designing a website but what about authentication? Well That's where firebase comes in, I've read a few articles and posts about how to integrate firebase with GatsbyJS but most of them didn't involve typescript support. They also failed to explain keeping a user logged in, or setting up private routes. It's important that authentication be tracked in the browser and app state. That's why my approach will provide further customization and help you follow security best practices, let's get started!&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting up the Gatsby project
&lt;/h2&gt;

&lt;p&gt;First, you'll want to add the Gatsby client globally, use either depending on preference but I prefer yarn:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# using YARN
yarn global add gatsby-cli
# using NPM
npm install gatsby-cli -g
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;And then create the new project with gatsby-cli:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gatsby new project-name https://github.com/resir014/gatsby-starter-typescript-plus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;There are a few different forks of the original gatsby-default starter but I chose this one because it had a decent number of stars and forks.&lt;/p&gt;

&lt;p&gt;Then change directories to be in the new project:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd project-name/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Then install the project dependencies:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# using NPM
npm install
# using YARN
yarn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;This may take a while to install all the dependencies but be patient... Once it's done open the project in your preferred text editor, personally I use VS code and if you're on the fence about what you should use I highly recommend it. You should then be able to start your project by running this from the project root.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# using NPM
npm start
# using YARN
yarn start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Open up a browser window and go to &lt;a href="http://localhost:8000"&gt;http://localhost:8000&lt;/a&gt; and you should see a basic landing page, fancy!&lt;/p&gt;
&lt;h2&gt;
  
  
  Getting Firebase set up
&lt;/h2&gt;

&lt;p&gt;Now we need to make a firebase account and add that to our project. Create a firebase account and follow this guide then come back here when you're done.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://firebase.google.com/docs/web/setup"&gt;https://firebase.google.com/docs/web/setup&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You now have a firebase project in the firebase console, now to add firebase to the Gatsby project:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# using YARN
yarn add firebase
# using NPM
npm install firebase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Now in the firebase console go into your project settings and find your app config once you have that create an env file in the Gatsby app project root and call it .env.development this will be your development environment file where you'll store secret or universal app information.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// .env.development

GATSBY_FIREBASE_APIKEY={YOUR_API_KEY}
GATSBY_FIREBASE_AUTHDOMAIN={YOUR_AUTHDOMAIN}
GATSBY_FIREBASE_DATABASEURL={YOUR_DATABASE_URL}
GATSBY_FIREBASE_PROJECTID={YOUR_PROJECTID}
GATSBY_FIREBASE_STORAGEBUCKET={YOUR_STORAGE_BUCKET}
GATSBY_FIREBASE_MESSAGINGSENDERID={YOUR_MESSAGING_SENDER_ID}
GATSBY_FIREBASE_APPID={YOUR_APPID}
GATSBY_FIREBASE_MEASUREMENTID={YOUR_MEASUREMENTID}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;You should be able to find all these values from the config you found earlier in the firebase project console. Now in the src folder add a services folder and create a firebase provider file called FirebaseProvider.tsx this will be our firebase provider that will store and pass the firebase context we'll create for use throughout the app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    // FirebaseProvider.tsx

    import React from 'react'
    import firebase from 'firebase'

    // Your config that you stored in the env file.
    const firebaseConfig = {
     apiKey: process.env.GATSBY_FIREBASE_APIKEY,
     appId: process.env.GATSBY_FIREBASE_APPID,
     authDomain: process.env.GATSBY_FIREBASE_AUTHDOMAIN,
     databaseURL: process.env.GATSBY_FIREBASE_DATABASEURL,
     measurementId: process.env.GATSBY_FIREBASE_MEASUREMENTID,
     messagingSenderId: process.env.GATSBY_FIREBASE_MESSAGINGSENDERID,
     projectId: process.env.GATSBY_FIREBASE_PROJECTID,
     storageBucket: process.env.GATSBY_FIREBASE_STORAGEBUCKET
    }
    // The type definition for the firebase context data.

    export interface FirebaseContextData {
     isInitialized: boolean
     firebase: typeof firebase
     authToken: string | null
     setAuthToken: (authToken: string) =&amp;gt; void
    }
    // The firebase context that will store the firebase instance and other useful variables.

    export const FirebaseContext = React.createContext&amp;lt;FirebaseContextData&amp;gt;({
     authToken: null,
     firebase,
     isInitialized: false,
     setAuthToken: () =&amp;gt; {}
    })

    // The provider that will store the logic for manipulating the firebase instance and variables.

    export const FirebaseProvider: React.FC = ({ children }) =&amp;gt; {
     const [isInitialized, setIsInitialized] = React.useState(false)

    // If we have a window and the authToken already exists in localstorage then initialize the authToken value otherwise null.

    const [authToken, setAuthToken] = React.useState&amp;lt;FirebaseContextData['authToken']&amp;gt;(
     typeof window === 'object' ? window.localStorage.getItem('authToken') : null
     )

     // If firebase has not been initialized then initialize it.
     if (!firebase.apps.length) {
     firebase.initializeApp(firebaseConfig)
     setIsInitialized(true)
     }

     // A method for setting the authToken in state and local storage.
     const onSetAuthToken = (token: string) =&amp;gt; {
     setAuthToken(token)
     localStorage.setItem('authToken', token)
     }

     // If we have the window object and there is no authToken then try to get the authToken from local storage.
     React.useEffect(() =&amp;gt; {
     if (typeof window === 'object' &amp;amp;&amp;amp; !authToken) {
     const token = window.localStorage.getItem('authToken')

       if (token) {
         onSetAuthToken(token)
         }
       }
    }, [authToken])

    return (
     &amp;lt;FirebaseContext.Provider
     value={{
     authToken,
     firebase,
     isInitialized,
     setAuthToken: onSetAuthToken
     }}&amp;gt;
     {children}
     &amp;lt;/FirebaseContext.Provider&amp;gt;
     )
    }

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

&lt;/div&gt;



&lt;p&gt;This might seem complicated but it really only does a few things.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;It initializes the firebase app&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It sets up the context that will provide a reference to the firebase instance&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It creates state and set state methods for tracking authentication&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It provides the context with the firebase instance to the rest of the app&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more on contexts and how they work: &lt;a href="https://reactjs.org/docs/context.html"&gt;https://reactjs.org/docs/context.html&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the firebase context
&lt;/h2&gt;

&lt;p&gt;Inside the services folder create an index.ts file that will export all of our services.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// index.ts

export { FirebaseContext, FirebaseProvider } from './FirebaseProvider'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This exports the context and provider. Then inside the components folder find the LayoutRoot.tsx file and wrap the provider around it.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// LayoutRoot.tsx

import * as React from 'react'
import { Global, css } from '@emotion/core'
import { FirebaseProvider } from '../services'
import styled from '@emotion/styled'
import normalize from '../styles/normalize'

const StyledLayoutRoot = styled.div`
 display: flex;
 flex-direction: column;
 min-height: 100vh;
`
interface LayoutRootProps {
 className?: string
}

const LayoutRoot: React.FC&amp;lt;LayoutRootProps&amp;gt; = ({ children, className }) =&amp;gt; (
 &amp;lt;FirebaseProvider&amp;gt;
 &amp;lt;Global styles={() =&amp;gt; css(normalize)} /&amp;gt;
 &amp;lt;StyledLayoutRoot className={className}&amp;gt;{children}&amp;lt;/StyledLayoutRoot&amp;gt;
 &amp;lt;/FirebaseProvider&amp;gt;
)

export default LayoutRoot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will provide our context to the rest of the app. Also in the services folder create a types folder and inside that create a file called AuthError.tsx that will export the error type we'll need in a minute.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// AuthError.tsx

import { FirebaseError as FBError } from 'firebase'
export type AuthError = FBError &amp;amp; Error
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And then export that from the root of services like so.&lt;/p&gt;

&lt;p&gt;In the index.ts file of the services folder:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// index.ts

export { FirebaseContext, FirebaseProvider } from './FirebaseProvider'
export { AuthError } from './types/AuthError'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is an error type that will come in handy when catching errors that we might get from firebase authentication. Now find the Page component in src/components/Page.tsx and make some changes.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Page.tsx

import * as React from 'react'
import { FirebaseContext } from '../services'
import styled from '@emotion/styled'
import { dimensions } from '../styles/variables'

const StyledPage = styled.div`
 display: block;
 flex: 1;
 position: relative;
 padding: ${dimensions.containerPadding}rem;
 margin-bottom: 3rem;
`

interface PageProps {
 className?: string
}

const Page: React.FC&amp;lt;PageProps&amp;gt; = ({ children, className }) =&amp;gt; { 
 const { isInitialized } = React.useContext(FirebaseContext)
 console.log(`firebase instance is ${isInitialized ? 'initialized' : 'not initialized'}`)

 return(&amp;lt;StyledPage className={className}&amp;gt;{children}&amp;lt;/StyledPage&amp;gt;)
}

export default Page
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You may have to refresh the page but you should get a message logged in the console that says the instance has been initialized. You can go ahead and remove these changes if it worked. Now we will add some more dependencies for creating the login page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making it stylish! The login page edition
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# using YARN
yarn add @material-ui/core @fortawesome/react-fontawesome @fortawesome/free-brands-svg-icons
# using NPM
npm install @material-ui/core @fortawesome/react-fontawesome @fortawesome/free-brands-svg-icons
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We'll need material and font awesome to quickly make the login page. In components create a Login.tsx file that will handle our login logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    // Login.tsx

    import { AuthError, FirebaseContext } from '../services'
    import { Button, FormControl, FormHelperText, Input, InputLabel } from '@material-ui/core'
    import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
    import React from 'react'
    import { auth } from 'firebase'
    import { faGoogle } from '@fortawesome/free-brands-svg-icons'
    import { navigate } from 'gatsby'

    const Login: React.FC = () =&amp;gt; {
     // get the variables we need for authentication.
     const { firebase, authToken, setAuthToken } = React.useContext(FirebaseContext)
     // setup some state variables for login
     const [email, setEmail] = React.useState&amp;lt;string&amp;gt;('')
     const [password, setPassword] = React.useState&amp;lt;string&amp;gt;('')

     // The method for handling google authentication
     const handleGoogleAuth = React.useCallback(
         async (event: React.MouseEvent&amp;lt;HTMLButtonElement, MouseEvent&amp;gt;) =&amp;gt; {
     try {
        event.preventDefault()
        const provider = new firebase.auth.GoogleAuthProvider()
    // get the credential from the google auth.
        const { credential } = await   firebase.auth().signInWithPopup(provider)
     // if we have a credential then get the access token and set it in state.

        if (credential) {
    // This has to be assigned to the oathcredential type so that we can get the accessToken property.

    const { accessToken } = credential as auth.OAuthCredential
     setAuthToken(accessToken as string)
       }
     } catch (e) {
     console.log(e)
       }
     },
     [firebase, setAuthToken]
     )

     // Method for signing up and logging in.
     const handleSignupAndLogin = React.useCallback(
     async (event: React.MouseEvent&amp;lt;HTMLButtonElement, MouseEvent&amp;gt;) =&amp;gt; {
     let authError: AuthError | undefined
    try {
     event.preventDefault()
     // Try to create a new user with the email and password.
     const { user } = await firebase.auth().createUserWithEmailAndPassword(email, password)

    // If successful and we have a user the set the authToken.
     if (user) {
     const { refreshToken } = user
     setAuthToken(refreshToken)
       }
     // If there is an error set the authError to the new error
     } catch (error) {
     authError = error
     } finally {
     // If there is an authError and the code is that the email is already in use, try to sign 
    // the user in with the email and password instead.

     if (authError?.code === 'auth/email-already-in-use') {
     const { user } = await firebase.auth().signInWithEmailAndPassword(email, password)
     // We've been here before... set the authToken if there is a user.

           if (user) {
           const { refreshToken } = user
           setAuthToken(refreshToken)
           }
         }
       }
     },
     [email, password, firebase, setAuthToken]
     )

    // Effect that will reroute the user to the index.tsx file if there is an authToken
     React.useEffect(() =&amp;gt; {
     if (authToken) {
     navigate('/')
     }
     }, [authToken])

    return (
     &amp;lt;form style={{ display: 'flex', flexDirection: 'column' }}&amp;gt;
       &amp;lt;FormControl&amp;gt;
         &amp;lt;InputLabel htmlFor="email"&amp;gt;Email address&amp;lt;/InputLabel&amp;gt;
         &amp;lt;Input id="email" aria-describedby="email-helper" value={email}
          onChange={(event) =&amp;gt; setEmail(event.currentTarget.value)} /&amp;gt;
         &amp;lt;FormHelperText id="email-helper"&amp;gt;We&amp;amp;apos;ll never share your email.&amp;lt;/FormHelperText&amp;gt;
       &amp;lt;/FormControl&amp;gt;
       &amp;lt;FormControl&amp;gt;
         &amp;lt;InputLabel htmlFor="password"&amp;gt;Password&amp;lt;/InputLabel&amp;gt;
          &amp;lt;Input id="password" value={password} onChange={(event) =&amp;gt;   setPassword(event.currentTarget.value)} /&amp;gt;
       &amp;lt;/FormControl&amp;gt;
       &amp;lt;Button type="submit" variant="contained" color="primary" style={{ marginTop: '10px' }} onClick={handleSignupAndLogin}&amp;gt;
     Login / Sign Up
       &amp;lt;/Button&amp;gt;
       &amp;lt;Button type="button" variant="contained" color="primary"
        style={{ marginTop: '10px' }} onClick={handleGoogleAuth}&amp;gt;
       &amp;lt;FontAwesomeIcon icon={faGoogle} style={{ marginRight: '10px' }} /&amp;gt;
     Login With Google
     &amp;lt;/Button&amp;gt;
     &amp;lt;/form&amp;gt;
     )
    }

    export default Login

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

&lt;/div&gt;



&lt;p&gt;The login component will handle sign in and sign up as well as google authentication, neat! Before all of this will work though you'll have to enable these sign in options from the firebase project console; there's a brief explanation of how to do this in the firebase documentation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://firebase.google.com/docs/auth/web/password-auth"&gt;https://firebase.google.com/docs/auth/web/password-auth&lt;/a&gt;&lt;br&gt;
&lt;a href="https://firebase.google.com/docs/auth/web/google-signin"&gt;https://firebase.google.com/docs/auth/web/google-signin&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once that's done you'll have to create the page that will use the login component we just created go into the pages folder and create a login.tsx file.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// login.tsx

import * as React from 'react'
import Page from '../components/Page'
import Container from '../components/Container'
import IndexLayout from '../layouts'
import Login from '../components/login'

const LoginPage = () =&amp;gt; (
 &amp;lt;IndexLayout&amp;gt;
 &amp;lt;Page&amp;gt;
 &amp;lt;Container&amp;gt;
 &amp;lt;Login /&amp;gt;
 &amp;lt;/Container&amp;gt;
 &amp;lt;/Page&amp;gt;
 &amp;lt;/IndexLayout&amp;gt;
)

export default LoginPage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Preventing users from seeing things they shouldn't
&lt;/h2&gt;

&lt;p&gt;Now, in components create a PrivateRoute.tsx file that we will use to prevent unauthenticated users from seeing content that they shouldn't be allowed to access.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// PrivateRoute.tsx

import * as React from 'react'
import { FirebaseContext } from '../services'
import { navigate } from 'gatsby'

interface PrivateRouteProps {
 path: string
}

const PrivateRoute: React.FC&amp;lt;PrivateRouteProps&amp;gt; = ({ children, path }) =&amp;gt; {
 const { authToken } = React.useContext(FirebaseContext)

if (!authToken &amp;amp;&amp;amp; window.location.href !== path) {
 navigate(path)
 return null
 }

return &amp;lt;&amp;gt;{children}&amp;lt;/&amp;gt;
}

export default PrivateRoute
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will re-route users to the login page if they try to access anything that is nested in this component. Finally, we just have to add this component to our index.tsx file in pages.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// index.tsx

import * as React from 'react'
import { Link } from 'gatsby'
import Page from '../components/Page'
import Container from '../components/Container'
import IndexLayout from '../layouts'
import { PrivateRoute } from '../components/PrivateRoute'

const IndexPage = () =&amp;gt; (
 &amp;lt;IndexLayout&amp;gt;
   &amp;lt;Page&amp;gt;
    &amp;lt;PrivateRoute path="/login"&amp;gt;
     &amp;lt;Container&amp;gt;
      &amp;lt;h1&amp;gt;Hi people&amp;lt;/h1&amp;gt;
      &amp;lt;p&amp;gt;Welcome to your new Gatsby site.&amp;lt;/p&amp;gt;
      &amp;lt;p&amp;gt;Now go build something great.&amp;lt;/p&amp;gt;
      &amp;lt;Link to="/page-2/"&amp;gt;Go to page 2&amp;lt;/Link&amp;gt;
     &amp;lt;/Container&amp;gt;
    &amp;lt;/PrivateRoute&amp;gt;
   &amp;lt;/Page&amp;gt;
 &amp;lt;/IndexLayout&amp;gt;
)

export default IndexPage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will tell the index page to re route to the login page if there isn't an authToken and therefore the user is not logged in. You'll still have to implement signing out but all that involves is making the token expire somehow. You'll probably want to separate the signin / signup flow at some point as well but this will get you a good start on authentication.&lt;/p&gt;

&lt;p&gt;That concludes this tutorial on GatsbyJS and firebase, this solution is an improvement on some of the other tutorials I've seen that don't use typescript or store the firebase instance in state. By tracking the authToken we get more control over the state and we can easily add new fields to our context.&lt;/p&gt;

&lt;p&gt;I hope you've learned something from this article and if you have any questions feel free to leave a comment below I will be writing more articles in the future on other topics that I feel haven't been covered well enough or that I struggled with, thanks for joining me!&lt;/p&gt;

&lt;p&gt;Please follow me on Twitter: &lt;a href="https://twitter.com/SquashBugler"&gt;@SquashBugler&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gatsby</category>
      <category>typescript</category>
      <category>react</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
