Obsidian as a CMS

This post will take roughly 3 minutes to read. It was first published on , and was last updated

A recent concept I shared on Farcaster garnered some interest, so I wanted to dive deeper into the details. This approach leverages several tools and services to create a decentralized, git-based “CMS” that enables editing content from virtually anywhere.

The core of the system is an Obsidian template that includes structured YAML frontmatter. File names use a Zettelkasten-inspired millisecond timestamp tag, along with a created timestamp and the initial post title.

<span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;</span>
<span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;title:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;quot;1671418753342&amp;quot;&lt;/span&gt;</span> 
<span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;created:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;quot;1671418753342&amp;quot;&lt;/span&gt;</span>
<span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;longform:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;</span>
<span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;published:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;</span>
<span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;</span>

Using the timestamp as the title allows most content to exist as a “journal” entry. More descriptive titles can be set when needed.

Obsidian is configured to automatically sync content with a private Github repo shortly after editing, providing continuous backup and versioning. A published flag in the frontmatter determines if an entry should be displayed publicly.

Upon pushing to the repo, a Github Action runs to upload any assets from the designated folder to a Cloudflare R2 bucket (similar to Amazon S3 but with a generous free tier).

<span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Cloudflare&lt;/span&gt;</span>
<span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;on:&lt;/span&gt;</span>
  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;push:&lt;/span&gt;</span>
    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;branches:&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;main&lt;/span&gt;</span>
  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;workflow_dispatch:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-literal&quot;&gt;null&lt;/span&gt;</span> 
<span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;jobs:&lt;/span&gt;</span>
  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;deploy:&lt;/span&gt;</span>
    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;runs-on:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;ubuntu-latest&lt;/span&gt;</span>
    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;steps:&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;actions/checkout@v3&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;R2&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Directory&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Upload&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;willfurstenau/r2-dir-upload@main&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;with:&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;accountid:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;quot;$&lt;span</span> <span class="hljs-string">class=&quot;hljs-template-variable&quot;&gt;{{</span> <span class="hljs-string">secrets.CF_ACCOUNT_ID</span> <span class="hljs-string">}}&lt;/span&gt;&amp;quot;&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;accesskeyid:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;quot;$&lt;span</span> <span class="hljs-string">class=&quot;hljs-template-variable&quot;&gt;{{</span> <span class="hljs-string">secrets.CF_ACCESS_KEY</span> <span class="hljs-string">}}&lt;/span&gt;&amp;quot;&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;secretaccesskey:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;quot;$&lt;span</span> <span class="hljs-string">class=&quot;hljs-template-variable&quot;&gt;{{</span> <span class="hljs-string">secrets.CF_SECRET_KEY</span> <span class="hljs-string">}}&lt;/span&gt;&amp;quot;&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;bucket:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;iam-bucket&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;source:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;quot;$&lt;span</span> <span class="hljs-string">class=&quot;hljs-template-variable&quot;&gt;{{</span> <span class="hljs-string">github.workspace</span> <span class="hljs-string">}}&lt;/span&gt;/Assets&amp;quot;&lt;/span&gt;</span> 
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;destination:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;/&lt;/span&gt;</span>

With content in the repo and assets on Cloudflare, we turn to NextJS to build the frontend. Two key queries come into play: getObsidianEntries and getObsidianEntry.

getObsidianEntries fetches all entries from the designated repo and directory:

&lt;span <span class="hljs-keyword">class</span>=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">export</span>&lt;<span class="hljs-regexp">/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;default&lt;/</span>span&gt; <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;</span>async<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;</span>function<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-title function_&quot;</span>&gt;</span>getObsidianEntries<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>(<span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-params&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>) {
  <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;</span>const<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span> token = process.&lt;span <span class="hljs-keyword">class</span>=<span class="hljs-string">&quot;hljs-property&quot;</span>&gt;env&lt;<span class="hljs-regexp">/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;NEXT_PUBLIC_GITHUB&lt;/</span>span&gt;;
  
  <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;</span>const<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span> { 
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;</span>data<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>: {
      <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;</span>repository<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>: {
        <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;</span>object<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>: { entries },
      },
    },
  } = <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;</span>await<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-title function_&quot;</span>&gt;</span>fetch<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>(<span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;</span>`https://api.github.com/graphql`<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>, {
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;</span>method<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>: <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;</span>`POST`<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>,
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;</span>headers<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>: {
      <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;</span><span class="hljs-symbol">&amp;quot;</span>Content-Type<span class="hljs-symbol">&amp;quot;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>: <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;</span>`application/json`<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>,
      <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-title class_&quot;</span>&gt;</span>Authorization<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>: <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;</span>`Bearer <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-subst&quot;</span>&gt;</span>${token}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>`<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>,
    },
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;</span>body<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>: <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-title class_&quot;</span>&gt;</span>JSON<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>.&lt;span <span class="hljs-keyword">class</span>=<span class="hljs-string">&quot;hljs-title function_&quot;</span>&gt;stringify&lt;/span&gt;({
      <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;</span>query<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>: <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;</span>`
      query fetchEntries($owner: String!, $name: String!) {
        repository(owner: $owner, name: $name) {
          object(expression: <span class="hljs-symbol">&amp;quot;</span>HEAD:Content/<span class="hljs-symbol">&amp;quot;</span>) {
            ... on Tree {
              entries {
                name
                object {
                  ... on Blob {
                    text
                  }
                }
              }
            }
          }
        }
      }  
            `<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>,
      <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;</span>variables<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>: {
        <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;</span>owner<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>: <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;</span>`GITHUB_USERNAME`<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>,
        <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;</span>name<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>: <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;</span>`REPO_NAME`<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>, 
        <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;</span>first<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>: <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;</span>100<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>,
      },
    }),
    <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;</span>next<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>: {
      <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;</span>revalidate<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>: <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;</span>1<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span> * <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;</span>30<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>,
    },
  }).&lt;span <span class="hljs-keyword">class</span>=<span class="hljs-string">&quot;hljs-title function_&quot;</span>&gt;then&lt;<span class="hljs-regexp">/span&gt;(&lt;span class=&quot;hljs-function&quot;&gt;(&lt;span class=&quot;hljs-params&quot;&gt;res&lt;/</span>span&gt;) =&amp;gt;&lt;<span class="hljs-regexp">/span&gt; res.&lt;span class=&quot;hljs-title function_&quot;&gt;json&lt;/</span>span&gt;());

  <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;</span>return<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span> entries;
}

A Github access token is required to query private repos via GraphQL (see Authenticating with GraphQL). Drilling into the repository object, filtered by the Content directory on the main branch, returns the desired entries. Next13’s revalidation flag keeps the content fresh.

Once entries are fetched, the content can be manipulated as needed. I parse the YAML frontmatter with gray-matter and render Markdown using react-remark, part of the expansive Unified/Remark ecosystem.

The second query, getObsidianEntry, is used for individual post pages:

&lt;span <span class="hljs-keyword">class</span>=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">export</span>&lt;<span class="hljs-regexp">/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;default&lt;/</span>span&gt; <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;</span>async<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;</span>function<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-title function_&quot;</span>&gt;</span>getObsidianEntry<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>(<span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-params&quot;</span>&gt;</span>slug: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-built_in&quot;</span>&gt;</span>any<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>) {
  <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;</span>const<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span> paths = <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;</span>await<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-title function_&quot;</span>&gt;</span>getObsidianEntries<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>();

  <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;</span>const<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span> _paths = <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;</span>await<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-title class_&quot;</span>&gt;</span>Promise<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>.&lt;span <span class="hljs-keyword">class</span>=<span class="hljs-string">&quot;hljs-title function_&quot;</span>&gt;all&lt;/span&gt;(paths);
  
  <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;</span>const<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span> entry = _paths.&lt;span <span class="hljs-keyword">class</span>=<span class="hljs-string">&quot;hljs-title function_&quot;</span>&gt;find&lt;<span class="hljs-regexp">/span&gt;(&lt;span class=&quot;hljs-function&quot;&gt;(&lt;span class=&quot;hljs-params&quot;&gt;entry: &lt;span class=&quot;hljs-built_in&quot;&gt;any&lt;/</span>span&gt;&lt;<span class="hljs-regexp">/span&gt;) =&amp;gt;&lt;/</span>span&gt; entry.&lt;span <span class="hljs-keyword">class</span>=<span class="hljs-string">&quot;hljs-property&quot;</span>&gt;slug&lt;/span&gt; === slug);

  <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;</span>return<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span> entry;
}  

Ideally this would directly query GraphQL for the specific entry needed, but I haven’t yet gotten that filter to work. As a temporary solution, the appropriate entry is filtered out using the page slug (Zettelkasten tag) as the key.

And there you have it - a decentralized approach to using Obsidian as a lightweight CMS. I hope these notes prove useful for anyone interested in setting up a similar system.