<?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: Matteo Rigon</title>
    <description>The latest articles on DEV Community by Matteo Rigon (@reegodev).</description>
    <link>https://dev.to/reegodev</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%2F36114%2F7f8a1d83-c27b-483f-9f9a-8fd72e078035.jpg</url>
      <title>DEV Community: Matteo Rigon</title>
      <link>https://dev.to/reegodev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/reegodev"/>
    <language>en</language>
    <item>
      <title>Looking at the new v-memo directive in Vue 3.2</title>
      <dc:creator>Matteo Rigon</dc:creator>
      <pubDate>Thu, 25 Nov 2021 17:53:30 +0000</pubDate>
      <link>https://dev.to/reegodev/looking-at-the-new-v-memo-directive-in-vue-32-5d6d</link>
      <guid>https://dev.to/reegodev/looking-at-the-new-v-memo-directive-in-vue-32-5d6d</guid>
      <description>&lt;p&gt;The release of Vue 3.2 introduced some new functionalities, mainly related to performance and optimisations. One of these features is a new directive called &lt;strong&gt;v-memo&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is v-memo?
&lt;/h2&gt;

&lt;p&gt;v-memo, as the name suggests, is a new directive related to the &lt;em&gt;memoization&lt;/em&gt; of parts of a template.&lt;br&gt;
If you are not familiar with the term "memoization", wikipedia describes it as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In computing, memoization or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In Vue this description sounds really like what &lt;code&gt;computed&lt;/code&gt; properties already do.&lt;br&gt;
In fact v-memo can be seen as a computed property for parts of a template!&lt;/p&gt;
&lt;h2&gt;
  
  
  Using v-memo
&lt;/h2&gt;

&lt;p&gt;v-memo accepts a single parameter, which should be an array of dependencies. The element that uses this directive and all its descendants will only be re-rendered when one of the dependencies change. &lt;br&gt;
For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;v-memo=&lt;/span&gt;&lt;span class="s"&gt;"[dep1, dep2]"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  ...
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that if you don't provide dependencies , ie:&lt;code&gt;v-memo="[]"&lt;/code&gt;, you obtain the same functionality as &lt;code&gt;v-once&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Examples
&lt;/h2&gt;

&lt;p&gt;Since v-memo is mainly useful for performance reasons, one of the best scenarios where you'd want to use it is when rendering huge lists of items with &lt;code&gt;v-for&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"item in list"&lt;/span&gt; &lt;span class="na"&gt;:key=&lt;/span&gt;&lt;span class="s"&gt;"item.id"&lt;/span&gt; &lt;span class="na"&gt;v-memo=&lt;/span&gt;&lt;span class="s"&gt;"[item.id === selected]"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;ID: {{ item.id }} - selected: {{ item.id === selected }}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example we are telling v-memo to only re-render an item if it has been selected or deselected, all other items will be skipped entirely. &lt;br&gt;
We don't need to include &lt;code&gt;item.id&lt;/code&gt; in the dependency array since the sub-template of every item is already keyed,  so it generates a separate sub-tree.&lt;br&gt;
Another thing to keep in mind is that v-memo is only useful in a v-for if you are using some kind of interpolation. If for example you are rendering a list of components like &lt;code&gt;&amp;lt;MyComponent v-for="item in list" :key="item.id" v-memo /&amp;gt;&lt;/code&gt;, you are not going to benefit from v-memo as the diffing is made inside each component.&lt;/p&gt;

&lt;p&gt;I've prepared an example on StackBlitz that shows visually how v-memo helps with performance in lists: &lt;a href="https://stackblitz.com/edit/vue-v-memo"&gt;https://stackblitz.com/edit/vue-v-memo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vue</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Incremental Static Regeneration on Cloudflare Workers</title>
      <dc:creator>Matteo Rigon</dc:creator>
      <pubDate>Mon, 22 Nov 2021 17:08:35 +0000</pubDate>
      <link>https://dev.to/reegodev/incremental-static-regeneration-on-cloudflare-workers-5b3c</link>
      <guid>https://dev.to/reegodev/incremental-static-regeneration-on-cloudflare-workers-5b3c</guid>
      <description>&lt;p&gt;I've been testing Cloudflare Workers for a bit and was immediately impressed by their performance, but one thing that really stood out for me was their extreme flexibility.&lt;br&gt;
Lately, i've realised that with Workers it's really easy to achieve &lt;strong&gt;Incremental Static Regeneration&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  What is Incremental Static Regeneration?
&lt;/h2&gt;

&lt;p&gt;Incremental Static Regeneration, or ISR for short, is a technique first introduced by Vercel to improve build times for large static websites with a lot of pages.&lt;br&gt;&lt;br&gt;
Instead of rendering every page upfront at build time, you render pages on demand when they are requested, and then persist the response along with the rest of the site assets, effectively serving static pages after the first render.&lt;/p&gt;

&lt;p&gt;This is a really cool feature that allows Jamstack sites to scale indefinitely.&lt;/p&gt;
&lt;h2&gt;
  
  
  How do we achieve ISR on Cloudflare Workers?
&lt;/h2&gt;

&lt;p&gt;The real star of the show are not Workers per-se, but rather Workers KV.&lt;br&gt;
Let's read their official description:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Workers KV is a global, low-latency, key-value data store. It supports exceptionally high read volumes with low-latency, making it possible to build highly dynamic APIs and websites which respond as quickly as a cached static file would.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To be honest, this description is not really helpful in describing how awesome KV is. To me, this seems just like a key-value store that applications and websites *may* need, but looks more like a niche tool.&lt;/p&gt;

&lt;p&gt;In reality, KV is more like a distributed storage system like S3, but optimised to work at the edge.&lt;br&gt;
In fact, you can store any type of data in KV, and Workers actually use KV to store and serve static assets.&lt;/p&gt;

&lt;p&gt;After figuring out how useful KV is, an idea popped into my head: this is a great way to achieve ISR!&lt;/p&gt;
&lt;h2&gt;
  
  
  A Workers example with ISR
&lt;/h2&gt;

&lt;p&gt;After a bit of theory, lets see some code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Remove leading slashes&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;+/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Try to serve a static asset from KV&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;asset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;__STATIC_CONTENT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// ignore errors and fall back to app rendering&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Fall back to app rendering&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// This part is framework-specific.&lt;/span&gt;
            &lt;span class="c1"&gt;// Your favourite framework will render the page&lt;/span&gt;
            &lt;span class="c1"&gt;// based on the request path&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rendered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// ISR is achieved here:&lt;/span&gt;
                &lt;span class="c1"&gt;// on successful renders we store the response in KV.&lt;/span&gt;
                &lt;span class="c1"&gt;// Subsequent requests will be served from the store&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;__STATIC_CONTENT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error rendering route: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hopefully the code is self-explanatory, but let's summarize the key points:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We check if the asset is already in the KV store. If it is, we return it.&lt;/li&gt;
&lt;li&gt;If we did not find the asset in KV, we render the page and store it.&lt;/li&gt;
&lt;li&gt;On the next request for the same page, it will be served from KV.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As you can see, we achieved ISR with this really simple Workers code.&lt;/p&gt;

&lt;p&gt;A few notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Please do not use this code for production. Serving static assets is a bit more complicated than this, as you have to carefully set response headers to obtain the best possible caching behaviour on browsers. To serve static assets properly i suggest using this library &lt;a href="https://github.com/cloudflare/kv-asset-handler"&gt;https://github.com/cloudflare/kv-asset-handler&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developers.cloudflare.com/workers/runtime-apis/kv#creating-expiring-keys"&gt;You can pass a third argument to the KV &lt;code&gt;put&lt;/code&gt; method&lt;/a&gt; to provide a TTL or expiration date to re-render static pages after a certain amount of time. I highly suggest giving your static pages a limited lifetime, otherwise if content changes you need to purge KV keys manually (either through the dashboard or via API)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;__STATIC_CONTENT&lt;/code&gt; is a special KV namespace that is always available to all Workers and is used to store static assets served by your application. For Cloudflare Pages there is a similar namespace called &lt;code&gt;ASSETS&lt;/code&gt;, although it is exposed with a different API and i'm still figuring out if it has a method for writing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Beyond simple ISR
&lt;/h2&gt;

&lt;p&gt;The code sample above only shows a simple version of ISR, but we can actually go further:&lt;/p&gt;

&lt;h4&gt;
  
  
  Selective ISR
&lt;/h4&gt;

&lt;p&gt;We can store rendered pages selectively on KV, making our website fully hybrid. &lt;br&gt;
Think about an e-commerce website with a blog: we can apply ISR only to the blog pages since their content never changes, and use normal SSR for product pages since their content may change frequently ( stock quantity, prices )&lt;/p&gt;
&lt;h4&gt;
  
  
  Stale while revalidate
&lt;/h4&gt;

&lt;p&gt;If our content changes frequently, but we still want the benefit of hitting a static page, we can apply a &lt;em&gt;stale while revalidate&lt;/em&gt; (SWR) logic: before returning a page from the store, we can re-render that page and store the new content on KV. &lt;br&gt;
We still return the stale version, but the next request will find the updated version already stored.&lt;/p&gt;

&lt;p&gt;When implementing this technique we can also use two micro-optimisations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A TTL logic that re-renders a stale page only every X seconds, depending how often our content changes. We can use KV metadata to store when the page was rendered.&lt;/li&gt;
&lt;li&gt;Avoid the longer response penalty of re-rendering by leveraging the &lt;code&gt;waitUntil&lt;/code&gt; method.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sample SWR implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SWR_TTL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="c1"&gt;// Our static pages will be re-rendered every 60 seconds&lt;/span&gt;

&lt;span class="c1"&gt;// This function renders a page and stores it to KV with additional metadata&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;renderAndStorePage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// This part is framework-specific.&lt;/span&gt;
        &lt;span class="c1"&gt;// Your favourite framework will render the page&lt;/span&gt;
        &lt;span class="c1"&gt;// based on the request path&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rendered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;__STATIC_CONTENT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;})&lt;/span&gt;
                &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error rendering route: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Remove leading slashes&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;+/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Try to serve a static asset from KV&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;__STATIC_CONTENT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getWithMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="c1"&gt;// TODO: Determine if request is for a page&lt;/span&gt;
                    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createdAt&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;SWR_TTL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="nx"&gt;renderAndStorePage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// ignore errors and fall back to app rendering&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Fall back to app rendering&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;renderAndStorePage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;I've created a small example application that uses ISR on Cloudflare Workers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sveltekit-isr.reego.workers.dev"&gt;https://sveltekit-isr.reego.workers.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is made with Svelte Kit, you can find the source code &lt;a href="https://github.com/reegodev/sveltekit-isr-cloudflare-workers"&gt;here&lt;/a&gt;.&lt;br&gt;
Every page has a top red bar ( or bottom if you are reading from a smartphone) that shows when the page was rendered and for how long it is stored.&lt;/p&gt;

&lt;h2&gt;
  
  
  References &amp;amp; links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://vercel.com/docs/concepts/next.js/incremental-static-regeneration"&gt;Vercel's Incremental Static Regeneration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.cloudflare.com/workers/"&gt;Cloudflare Workers docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.cloudflare.com/workers/runtime-apis/kv"&gt;Cloudflare Workers KV docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/stale-while-revalidate/"&gt;Stale while revalidate on web.dev&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>jamstack</category>
      <category>cloudflare</category>
      <category>experiments</category>
    </item>
    <item>
      <title>Using Github discussions as your blog engine</title>
      <dc:creator>Matteo Rigon</dc:creator>
      <pubDate>Sat, 06 Nov 2021 14:59:21 +0000</pubDate>
      <link>https://dev.to/reegodev/using-github-discussions-as-your-blog-engine-2gmj</link>
      <guid>https://dev.to/reegodev/using-github-discussions-as-your-blog-engine-2gmj</guid>
      <description>&lt;p&gt;When i first thought about having a personal blog, i started wondering how i wanted my authoring experience to be like, and although there are plenty of great tools, &lt;strong&gt;i still wanted to find something smart and creative&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I found out that GitHub discussions are really powerful and their API gives us (almost) all the right tools to create a complete blog engine.&lt;br&gt;
In fact my own blog &lt;a href="https://reego.dev/" rel="noopener noreferrer"&gt;https://reego.dev/&lt;/a&gt; is completely powered by GitHub discussions!&lt;/p&gt;
&lt;h2&gt;
  
  
  🔍 Initial research
&lt;/h2&gt;

&lt;p&gt;After evaluating a few blog solutions, i thought: "I'll just use GitHub and version my posts as Markdown files".&lt;br&gt;&lt;br&gt;
The authoring experience is pretty straightforward: create a Markdown file in your repository and push it. You can even create files directly from the GitHub UI, and you also get a nice Markdown preview.&lt;br&gt;&lt;br&gt;
Although this method works, i still wanted something better.&lt;/p&gt;

&lt;p&gt;Versioning Markdown files on a repository didn't feel cool to me, but GitHub still has a few alternatives that kept me interested.&lt;br&gt;&lt;br&gt;
At first, i thought about using issues as blog posts: they have a title, a Markdown editor, labels (which are tags in every aspect ), comments, reactions, and a good search API. &lt;br&gt;&lt;br&gt;
Unfortunately, issues miss something critical for a blog engine: everyone can create an issue on a public repo. 😱&lt;br&gt;&lt;br&gt;
 After some research, i wasn't able to find a way around this problem without involving complex actions and hooks, so i looked at a similar alternative: discussions.&lt;/p&gt;
&lt;h2&gt;
  
  
  ✅  The right tool for the wrong job
&lt;/h2&gt;

&lt;p&gt;GitHub discussions share all the cool things that issues bring, plus a few more. Lets do a quick recap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A title&lt;/li&gt;
&lt;li&gt;a Markdown editor with preview&lt;/li&gt;
&lt;li&gt;labels (tags)&lt;/li&gt;
&lt;li&gt;comments&lt;/li&gt;
&lt;li&gt;reactions&lt;/li&gt;
&lt;li&gt;powerful search&lt;/li&gt;
&lt;li&gt;writing permissions&lt;/li&gt;
&lt;li&gt;categories&lt;/li&gt;
&lt;li&gt;upvotes&lt;/li&gt;
&lt;li&gt;pinning&lt;/li&gt;
&lt;li&gt;subscribe for updates&lt;/li&gt;
&lt;li&gt;decent mobile authoring experience with the GitHub mobile app&lt;/li&gt;
&lt;li&gt;infinite integrations through actions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After a bit of tinkering i was able to create a simple blog engine by following these simple steps:&lt;/p&gt;
&lt;h2&gt;
  
  
  1️⃣  Edit discussion categories
&lt;/h2&gt;

&lt;p&gt;Discussion categories are important for our goal: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;They are mandatory, so you need at least one, even if you don't plan on using categories in your blog; &lt;/li&gt;
&lt;li&gt;You need to edit for each category their "Discussion format" and select "Announcement". &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When setting a category as announcement, only maintainers of the repository can create discussions in that category, and if all your categories are set as announcement, the "New discussion" button will disappear for everyone besides maintainers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F28622728%2F138962955-5253be21-7ecf-4142-b81d-3f1f8fde884f.png" class="article-body-image-wrapper"&gt;&lt;img alt="Setting the format of a category to Announcement in the GitHub UI" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F28622728%2F138962955-5253be21-7ecf-4142-b81d-3f1f8fde884f.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  2️⃣  Create our first post
&lt;/h2&gt;

&lt;p&gt;To query our post we have to use its internal id, which is not visible through the default GitHub UI, and would also give us a non-pleasant URL format, ie: &lt;code&gt;/blog/D_kwDOGN2Rdc4AN6ar&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
To use pretty URLs we have to be a bit creative: the method i chose is to add a frontmatter section in the body of my posts. GitHub Markdown preview does not complain about it, so it's an effective way to allow us to add metadata to our posts. &lt;/p&gt;

&lt;p&gt;Using the frontmatter section, we can add a &lt;code&gt;slug&lt;/code&gt; property that will serve as the search term to fetch our post. (I've also added another frontmatter property that serves as a short description of the post)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that the uniqueness of the slug is entirely up to us. As long as our blog does not have hundreds of posts it won't be a problem.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is what our first post looks like in Markdown:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;our-first-blog-post&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Writing&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;our&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;first&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;blog&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;post&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;as&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;GitHub&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;discussion"&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gu"&gt;## This is a post&lt;/span&gt;
How cool is that?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3️⃣  Query our post using the GitHub API
&lt;/h2&gt;

&lt;p&gt;We can query our post with a simple Graphql query (REST API is not available for new GitHub features like discussions):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DISCUSSION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;edges&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Discussion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;createdAt&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;updatedAt&lt;/span&gt;&lt;span class="w"&gt;

            &lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

            &lt;/span&gt;&lt;span class="n"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="n"&gt;edges&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our query variable will contain the following string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;repo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reegodev/reego.dev&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;our-first-blog-post&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;variables&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`"slug: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" in:body repo:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query basically searches the first discussion that contains the slug of our post in the body and is included in a specific repo.&lt;br&gt;
After running this query, we should be able to retrieve our post and render it using our favorite Markdown parser.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't forget to create a personal access token with all the "repo" permissions to send along as a bearer token&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  4️⃣  Embed discussion comments ( optional )
&lt;/h2&gt;

&lt;p&gt;To embed discussion comments directly into our articles i've found an amazing project: &lt;a href="https://github.com/giscus/giscus" rel="noopener noreferrer"&gt;Giscus&lt;/a&gt;.&lt;br&gt;
It fetches comments using the GitHub API, and lets your visitors post directly from your website with their GitHub account.&lt;br&gt;
Their website allows you to create the Giscus configuration by answering a few questions, mine ended up being this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://giscus.app/client.js"&lt;/span&gt;
      &lt;span class="na"&gt;data-repo=&lt;/span&gt;&lt;span class="s"&gt;"reegodev/reego.dev"&lt;/span&gt;
      &lt;span class="na"&gt;data-repo-id=&lt;/span&gt;&lt;span class="s"&gt;"R_kgDOGTk7pw"&lt;/span&gt;
      &lt;span class="na"&gt;data-mapping=&lt;/span&gt;&lt;span class="s"&gt;"number"&lt;/span&gt;
      &lt;span class="na"&gt;data-term=&lt;/span&gt;&lt;span class="s"&gt;"{ post.number }"&lt;/span&gt;
      &lt;span class="na"&gt;data-reactions-enabled=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt;
      &lt;span class="na"&gt;data-emit-metadata=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;
      &lt;span class="na"&gt;data-theme=&lt;/span&gt;&lt;span class="s"&gt;"dark_dimmed"&lt;/span&gt;
      &lt;span class="na"&gt;data-lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;
      &lt;span class="na"&gt;crossorigin=&lt;/span&gt;&lt;span class="s"&gt;"anonymous"&lt;/span&gt;
      &lt;span class="na"&gt;async&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After embedding this little snippet, comments will just show up, it's that easy!&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀  A great potential?
&lt;/h2&gt;

&lt;p&gt;Fetching a single post only scratches the surface of what we can achieve using discussions as our blog engine.&lt;br&gt;
There are many things that we can do with the GitHub API which i have not covered in this post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetching the list of posts ordered by creation date to populate our blog index&lt;/li&gt;
&lt;li&gt;Fetching pinned posts to show at the top of our blog index or homepage&lt;/li&gt;
&lt;li&gt;Fetching posts by tag to provide tag archives &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are interested in digging deeper into other functionalities you can browse the source code of my blog &lt;a href="https://github.com/reegodev/reego.dev" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope this post was an interesting read!&lt;br&gt;
Cheers!&lt;/p&gt;

</description>
      <category>github</category>
      <category>experiments</category>
    </item>
  </channel>
</rss>
