<?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: Trent Brew</title>
    <description>The latest articles on DEV Community by Trent Brew (@trentbrew).</description>
    <link>https://dev.to/trentbrew</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F881231%2F39c385ed-a2b6-4f90-98f6-926a029129b1.png</url>
      <title>DEV Community: Trent Brew</title>
      <link>https://dev.to/trentbrew</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/trentbrew"/>
    <language>en</language>
    <item>
      <title>I Built Obsidian for Coding</title>
      <dc:creator>Trent Brew</dc:creator>
      <pubDate>Mon, 29 Jun 2026 04:58:22 +0000</pubDate>
      <link>https://dev.to/trentbrew/a-polite-lie-told-to-gravity-50e0</link>
      <guid>https://dev.to/trentbrew/a-polite-lie-told-to-gravity-50e0</guid>
      <description>&lt;p&gt;Plants compete fiercely for sunlight. Many build sturdy, woody trunks to reach the sky. Some species are structurally flimsy. They grow as &lt;em&gt;vines&lt;/em&gt; that can't hold their own weight. Left alone, they sprawl across the dirt where their fruits rot or get devoured by tiny ground-dwelling pests.&lt;/p&gt;

&lt;p&gt;Humans built the &lt;strong&gt;trellis&lt;/strong&gt;, a vertical grid of wood or metal, to help the thing sustain itself so it can sustain &lt;em&gt;us&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This post is about the kind of work that grows like a vine (code, decisions, and the reasoning of AI agents) and the structure that lets it bear fruit instead of rotting on the ground.&lt;/p&gt;

&lt;h2&gt;
  
  
  30-second version
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Trellis is a local-first graph for things that grow but can't hold their own weight. Trellis Studio is the workspace where you tend them. Code, agents, and the reasoning behind every change live in one substrate. Boots with &lt;code&gt;npx trellis studio&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Vines
&lt;/h2&gt;

&lt;p&gt;Modern software is full of vines.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://player.mux.com/VuzmPzlVUfQet7AAFeb302kIAtzkSJNyuoF4302jlYFd8" width="710" height="399"&gt;
&lt;/iframe&gt;

&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code&lt;/strong&gt; is one. It grows fast, branches in every direction, and would happily eat a hard drive if you let it. It doesn't hold its own shape. What keeps it standing is the structure around it: a filesystem, a version control system, a build pipeline. Take those away and code becomes a pile of plaintext on the floor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project history&lt;/strong&gt; is a vine too. Every change has a reason. Most of those reasons live in PR comments, Slack threads, or somebody's memory. Pick any line of code in a six-month-old codebase and ask why it's there. Most of the time, the answer is on the dirt. Eaten.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI agents&lt;/strong&gt; are the newest vine, and the fastest-growing. They generate code, make decisions, take actions. Almost none of that reasoning survives the session. The agent's chat log is the only record, and it's already not enough. RAG-over-chat-history is what happens when you try to grow a vine across the dirt and pretend it's a garden.&lt;/p&gt;

&lt;p&gt;Each of these vines has its own little plot of dirt: its own tool, its own format, its own way of getting eaten. The connections between them live in your head.&lt;/p&gt;




&lt;h2&gt;
  
  
  Trellis is a trellis
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Trellis is a structural grid for the things that grow but can't hold their own weight in a software project.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Technically: a local-first, AI-native graph engine. Code lives in the graph as entities. Every change is an op: immutable, content-addressed, causally chained. Every decision (human or AI) is a record with a reason. History is a query, not a scroll. The whole thing lives in a &lt;code&gt;.trellis/&lt;/code&gt; folder you can back up with &lt;code&gt;cp -r&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The substrate doesn't care whether the vine is code or notes or agent reasoning. It just holds it up.&lt;/p&gt;

&lt;p&gt;I came to this from &lt;a href="https://brew.build/posts/filegraph/" rel="noopener noreferrer"&gt;Filegraph&lt;/a&gt;, which was the same idea applied to personal knowledge: files as nodes, agent actions as ops, reasoning you could inspect. The feature people loved was the canvas. The thing that &lt;em&gt;worked&lt;/em&gt; was the trellis underneath. Pulled out, generalized, it turns out to hold up software too.&lt;/p&gt;




&lt;h2&gt;
  
  
  Trellis Studio is the garden
&lt;/h2&gt;

&lt;p&gt;The engine runs by itself. The Studio is where you tend the vines.&lt;/p&gt;

&lt;p&gt;One screen, six tools that used to live in five different apps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Graph view.&lt;/strong&gt; Entities, ops, and links as a living graph. Click to inspect anything&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb2sca4jym75pajnwz3y1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb2sca4jym75pajnwz3y1.png" alt="Trellis Studio Graph View" width="800" height="588"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Code Editor &amp;amp; Rich Text.&lt;/strong&gt; Code entities in a tree, changes as causal steps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fklvcvi1jxi0dxobnqaq4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fklvcvi1jxi0dxobnqaq4.png" alt="Trellis Studio Code View" width="800" height="578"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;History &amp;amp; Diffing.&lt;/strong&gt; View history as a graph, with semantic diffing that understands structure, not lines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F117ycltyr9vqfibhvp82.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F117ycltyr9vqfibhvp82.png" alt="Trellis Studio History View" width="800" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Embedded agents.&lt;/strong&gt; Agents run inside the workspace. Every tool call is an op, not a chat log.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;iframe src="https://player.mux.com/p9nw02FfH5yNP02H68rqXj8XLT00V64vns9f15Rtt14g5k" width="710" height="399"&gt;
&lt;/iframe&gt;

&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Decision traces.&lt;/strong&gt; Capture the &lt;em&gt;why&lt;/em&gt; behind every change. Search precedent across past runs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjpx3n9suimqflujyk3ax.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjpx3n9suimqflujyk3ax.png" alt="Trellis Studio Decisions and Traces" width="800" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Issues and milestones.&lt;/strong&gt; Task tracking in the same graph as the code it ships with.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmebxvcbbz5amgjmbfqq0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmebxvcbbz5amgjmbfqq0.png" alt="Trellis Studio Issues and Milestones" width="800" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Idea garden.&lt;/strong&gt; Abandoned exploration stays searchable and revivable. Lost work isn't lost; it's just dormant.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic diffs.&lt;/strong&gt; Code-aware patches that understand structure, not lines. &lt;em&gt;Function renamed&lt;/em&gt;, not &lt;em&gt;12 lines changed&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same graph, six surfaces.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwiggw9ap1c5nw6vdjtji.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwiggw9ap1c5nw6vdjtji.png" alt="Trellis Studio Surfaces" width="800" height="896"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To boot it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx trellis studio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No account. No cloud. No signup. Your project, your machine, your data.&lt;/p&gt;




&lt;h2&gt;
  
  
  How the trellis is built
&lt;/h2&gt;

&lt;p&gt;Three parts.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Graph Engine
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4kv8ozhninrzvhbrwvzo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4kv8ozhninrzvhbrwvzo.png" alt="Trellis Studio graph engine" width="800" height="1215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Trellis is an EAV store (entities, attributes, values) plus a link index. Files, decisions, issues, and agent runs are all entities with stable IDs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F74wrhmphwdx4v9hpn3yn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F74wrhmphwdx4v9hpn3yn.png" alt="Trellis Studio Facts" width="800" height="575"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A fact:&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="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;decision:DEC-42&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rationale&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SEV-1 precedent + finance exception&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A link:&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="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;e1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;decision:DEC-42&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;affects&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;file:src/auth/session.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mutations produce immutable ops, content-addressed and causally chained. Reading the history of a file isn't &lt;code&gt;git log&lt;/code&gt;. It's a graph query, and you can ask things &lt;code&gt;git log&lt;/code&gt; can't: &lt;em&gt;what decisions affected this file, made by which agents, against what alternatives?&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Agent Harness
&lt;/h3&gt;

&lt;p&gt;&lt;iframe src="https://player.mux.com/p9nw02FfH5yNP02H68rqXj8XLT00V64vns9f15Rtt14g5k" width="710" height="399"&gt;
&lt;/iframe&gt;

&lt;/p&gt;

&lt;p&gt;When an agent runs inside the Studio, every tool call emits an op. The chat is the user-facing surface; the op log is the durable record. Asking "&lt;em&gt;why did the agent do that?&lt;/em&gt;" becomes a graph traversal, not an archaeological dig through scrollback.&lt;/p&gt;

&lt;p&gt;This is the part that breaks the "RAG over chat history" pattern. The agent doesn't have to remember its reasoning by searching its own logs. The reasoning is structured data in the same graph as the code it changed. Two composable steps from question to answer. No hallucination. No black box.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The Filesystem
&lt;/h3&gt;

&lt;p&gt;&lt;iframe src="https://player.mux.com/fMk012K2QDcMPLb6dhwioyc0000h3gsfNd7Nxva3hEN801A" width="710" height="399"&gt;
&lt;/iframe&gt;

&lt;/p&gt;

&lt;p&gt;The whole engine fits in a &lt;code&gt;.trellis/&lt;/code&gt; folder. SQLite for kernel state, JSON for entity collections, content-addressed blobs for ops. The filesystem is the source of truth; the graph is a materialized view, rebuilt on startup.&lt;/p&gt;

&lt;p&gt;Backup is &lt;code&gt;cp -r&lt;/code&gt;. Sync is optional. There's a server you can run for collaboration, but it's the second-best mode. The first-best mode is your machine.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the trellis holds up
&lt;/h2&gt;

&lt;p&gt;Here's the pattern I can't unsee.&lt;/p&gt;

&lt;p&gt;The biggest unsolved problem in AI tooling right now is &lt;strong&gt;memory&lt;/strong&gt;: not the model's context window, but durable structure an agent can return to in a week and act on. Every team building AI products is hitting the same wall: their agents are brilliant in the moment and amnesiac five minutes later.&lt;/p&gt;

&lt;p&gt;The current answers are all RAG over the agent's own output. Store the chat log, embed it, retrieve relevant fragments. This works for vibes-level recall. It fails the moment you need anything causal: &lt;em&gt;which decisions affected the auth subsystem last quarter and were later reversed?&lt;/em&gt; That's a graph query, not a similarity search.&lt;/p&gt;

&lt;p&gt;The way through is a structure that's legible to both the agent and you. Agents need durable memory. Humans need to read it. If those are different systems, they desync. If they're the same graph, the agent's reasoning is in the same shape as the codebase it's modifying. You can read either one without translation.&lt;/p&gt;

&lt;p&gt;That's what the trellis is holding up: one graph that holds the work of building software, structured enough that an agent can query it and a human can read it. One garden, two gardeners.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it out
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The CLI:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx trellis init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The web client:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx trellis studio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe src="https://player.mux.com/Wt01OmK01xoutHvg01gjKx7ksEH3700gz5JblihjF9j1ySs" width="710" height="399"&gt;
&lt;/iframe&gt;

&lt;/p&gt;

&lt;p&gt;Requires Node 18+ and git. Docs at &lt;a href="https://trellis.computer" rel="noopener noreferrer"&gt;trellis.computer&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Code, decisions, and agent reasoning are vines.&lt;/strong&gt; They don't hold their own weight. Build them a structure or accept that most of them will rot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The substrate is the durable bet, not the surface.&lt;/strong&gt; Filegraph's canvas was the photogenic part. The substrate underneath was what was actually general.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local-first solves "agent memory" better than RAG-over-cloud.&lt;/strong&gt; Embeddings work for similarity. They don't work for causal claims.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A graph you own beats a vector store you rent.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filegraph's canvas can come back.&lt;/strong&gt; As a Studio mode, on a substrate that's done the unglamorous work first.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Canvas mode.&lt;/strong&gt; The Filegraph payoff (spatial entity arrangement, projections, voice) but inside the Studio, over the same graph as your code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosted Trellis.&lt;/strong&gt; For people who don't want to install anything. Local-first stays the default; hosted is for when you need to share the garden.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Broader semantic-patch coverage.&lt;/strong&gt; Today TrellisVCS handles TypeScript, JavaScript, Python, and Markdown deeply. More languages, more operations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiplayer.&lt;/strong&gt; Real-time collaboration on a shared graph, with op-level conflict resolution that's actually correct because it's working on structured data instead of lines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For deeper architecture writeups (the EAV model, semantic patching, the decision-trace schema), separate technical posts are coming.&lt;/p&gt;

&lt;p&gt;Docs: &lt;a href="https://trellis.computer" rel="noopener noreferrer"&gt;trellis.computer&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built at &lt;a href="https://turtle.tech" rel="noopener noreferrer"&gt;turtle.tech&lt;/a&gt;. Continuing the work of &lt;a href="https://brew.build/posts/filegraph/" rel="noopener noreferrer"&gt;Filegraph&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>tooling</category>
      <category>automation</category>
    </item>
    <item>
      <title>What I Learned Building a Knowledge Graph for AI Agents</title>
      <dc:creator>Trent Brew</dc:creator>
      <pubDate>Fri, 07 Nov 2025 22:51:04 +0000</pubDate>
      <link>https://dev.to/trentbrew/what-i-learned-building-a-knowledge-graph-for-ai-agents-3e65</link>
      <guid>https://dev.to/trentbrew/what-i-learned-building-a-knowledge-graph-for-ai-agents-3e65</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F83ksyhjcz8m4ge85fbpx.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F83ksyhjcz8m4ge85fbpx.gif" alt=" " width="600" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AI assistants scrape through TODO files, commit messages, and scattered notes, trying to piece together what blocks a feature. They guess. They miss critical dependencies and recent decisions.&lt;/p&gt;

&lt;p&gt;The fix: let agents query project knowledge like a database instead of parsing human prose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before: Context scattered across files&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# “What’s blocking the auth feature?”&lt;/span&gt;
&lt;span class="c"&gt;# AI scrapes TODO.md, commit messages, Slack. Guesses. You verify manually.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After: Query the graph&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;code-hq query &lt;span class="s1"&gt;'FIND ?b WHERE urn:task:auth-123 dependsOn* ?b AND ?b.taskStatus != "Done"'&lt;/span&gt;
&lt;span class="c"&gt;# → urn:task:db-456  (Setup database, assigned to Bob)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  The core problems
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Missing relationships&lt;/strong&gt;: TODO lists describe tasks in isolation. Real work is about dependencies, ownership, and ripple effects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context brittleness&lt;/strong&gt;: Rephrase a comment or move a task, and the AI's understanding breaks. No stable way to reference project state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Translation overhead&lt;/strong&gt;: Humans use Markdown. Agents need structured data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Solution: maintain two layers - one for humans (Markdown, UIs) and one for machines (structured graph). Keep them synchronized.&lt;/p&gt;

&lt;h1&gt;
  
  
  The approach
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1) Everything as a graph
&lt;/h2&gt;

&lt;p&gt;Projects aren't lists - they're webs of relationships. Represent everything as JSON-LD entities with clear connections:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@context"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://schema.org"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@graph"&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="nl"&gt;"@id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"urn:task:auth-123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Task"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fix authentication bug"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"taskStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"InProgress"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"assignee"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"urn:person:alice"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"dependsOn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"urn:task:db-456"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"dateModified"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2025-10-24T04:12:00Z"&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="nl"&gt;"@id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"urn:task:db-456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Task"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Setup database"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"taskStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Blocked"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"assignee"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"urn:person:bob"&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;h2&gt;
  
  
  2) A query language for exploring relationships
&lt;/h2&gt;

&lt;p&gt;A simple query language that traverses relationships and filters results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FIND ?t
WHERE
  ?t a Task ;
     dependsOn* ?b .
  ?b taskStatus != "Done" .
FILTER (?t = urn:task:auth-123)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3) CLI for humans
&lt;/h2&gt;

&lt;p&gt;A CLI that handles the tedious parts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;code-hq init
code-hq create task &lt;span class="s2"&gt;"Fix auth bug"&lt;/span&gt; &lt;span class="nt"&gt;--priority&lt;/span&gt; high &lt;span class="nt"&gt;--assignee&lt;/span&gt; trent
code-hq tasks &lt;span class="nt"&gt;--status&lt;/span&gt; blocked
code-hq people &lt;span class="nt"&gt;--role&lt;/span&gt; developer
code-hq show &lt;span class="nt"&gt;--view&lt;/span&gt; kanban
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4588f7mcl4nro3hsgbp9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4588f7mcl4nro3hsgbp9.gif" alt=" " width="759" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4) Generated views
&lt;/h2&gt;

&lt;p&gt;Generate human-readable views from the structured data:&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="gh"&gt;# Tasks (Generated)&lt;/span&gt;

&lt;span class="gu"&gt;## In Progress&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Fix authentication bug (@trent, high)

&lt;span class="gu"&gt;## Blocked&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Setup database (@trent)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Humans use Markdown. Agents get structured data. Both stay synchronized through the CLI.&lt;/p&gt;

&lt;h1&gt;
  
  
  Migration
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Parse TODO.md → tasks with inferred priority/assignee/dependencies&lt;/span&gt;
code-hq import todo ./TODO.md

&lt;span class="c"&gt;# Import GitHub issues (PLACEHOLDER: add repo)&lt;/span&gt;
code-hq import github &lt;span class="nt"&gt;--repo&lt;/span&gt; org/repo

&lt;span class="c"&gt;# Keep files in sync (graph → Markdown views)&lt;/span&gt;
code-hq render &lt;span class="nt"&gt;--views&lt;/span&gt; kanban,timeline
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Trade-offs
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Authoring cost&lt;/strong&gt; → CLI verbs (&lt;code&gt;create&lt;/code&gt;, &lt;code&gt;link&lt;/code&gt;, &lt;code&gt;set&lt;/code&gt;) and smart importers make structured data feel natural&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schema drift&lt;/strong&gt; → JSON-LD contexts with validation on write&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review friction&lt;/strong&gt; → Small, machine-stable diffs in &lt;code&gt;.code-hq/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team adoption&lt;/strong&gt; → Humans keep Markdown, agents get structure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VSCode only&lt;/strong&gt; → Visualization layer depends on VSCode extension for now (web client coming soon): &lt;a href="https://github.com/trentbrew/code-hq-vscode" rel="noopener noreferrer"&gt;code-hq-vscode&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Getting started
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start with a simple graph&lt;/span&gt;
code-hq init

&lt;span class="c"&gt;# Add a few tasks with relationships&lt;/span&gt;
code-hq create task &lt;span class="s2"&gt;"Test semantic workflows"&lt;/span&gt; &lt;span class="nt"&gt;--priority&lt;/span&gt; medium

&lt;span class="c"&gt;# Query to understand the current state&lt;/span&gt;
code-hq query &lt;span class="s1"&gt;'FIND ?t WHERE ?t a Task ; priority "high"'&lt;/span&gt;

&lt;span class="c"&gt;# See it from a human perspective&lt;/span&gt;
code-hq show &lt;span class="nt"&gt;--view&lt;/span&gt; kanban
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Architecture
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────┐
│  CLI (Human Interface)  │  Simple commands that update understanding
└────────────┬────────────┘
             │
┌────────────▼────────────┐
│  Semantic Graph Layer   │  JSON-LD with clear relationships
└────────────┬────────────┘
             │
┌────────────▼────────────┐
│     Query Engine        │  TQL for exploring connections
└────────────┬────────────┘
             │
┌────────────▼────────────┐
│     Agent Workflows     │  Standup summaries, PR reviews, planning
└─────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  IDE Integration
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Cursor
&lt;/h3&gt;

&lt;p&gt;Add to &lt;code&gt;.cursorrules&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;This project uses code-hq for task management.
Reference .code-hq/prompts/task-management.md for commands.
Always check existing tasks before creating duplicates.
Update task status when starting/finishing work.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Windsurf
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;.windsurf/workflows/codehq.md&lt;/code&gt;:&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;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;code-hq commands reference&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
Read &lt;span class="sb"&gt;`.code-hq/prompts/_index.md`&lt;/span&gt; for overview.
See &lt;span class="sb"&gt;`.code-hq/prompts/task-management.md`&lt;/span&gt; for details.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Claude Code
&lt;/h3&gt;

&lt;p&gt;Automatically sees &lt;code&gt;.code-hq/prompts/&lt;/code&gt; as context.&lt;br&gt;
Ask: "How do I manage tasks in this project?"&lt;/p&gt;

&lt;h1&gt;
  
  
  What's next
&lt;/h1&gt;

&lt;p&gt;code-hq solves developer workflows. The next step is specialized AI agents for marketing, finance, and ops, all sharing the same knowledge graph. One source of truth for the entire company.&lt;/p&gt;




&lt;h3&gt;
  
  
  CodeHQ: The CLI
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Source Code: &lt;a href="https://github.com/trentbrew/code-hq" rel="noopener noreferrer"&gt;https://github.com/trentbrew/code-hq&lt;/a&gt;&lt;br&gt;
NPM Package: &lt;a href="https://www.npmjs.com/package/code-hq" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/code-hq&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  CodeHQ: The VSCode Extension
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://open-vsx.org/extension/codehq/codehq-vscode" rel="noopener noreferrer"&gt;https://open-vsx.org/extension/codehq/codehq-vscode&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  TQL: The Query Language
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Source Code: &lt;a href="https://github.com/trentbrew/TQL" rel="noopener noreferrer"&gt;https://github.com/trentbrew/TQL&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  JSON-LD: JSON For Linked Data
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Docs: &lt;a href="https://json-ld.org/" rel="noopener noreferrer"&gt;https://json-ld.org/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>productivity</category>
      <category>vscode</category>
    </item>
    <item>
      <title>I didn't like React so I made it more like Svelte</title>
      <dc:creator>Trent Brew</dc:creator>
      <pubDate>Sat, 07 Jun 2025 21:29:21 +0000</pubDate>
      <link>https://dev.to/trentbrew/using-svelte-runes-in-react-3g48</link>
      <guid>https://dev.to/trentbrew/using-svelte-runes-in-react-3g48</guid>
      <description>&lt;p&gt;As a Vue developer, learning React initially felt like a step backward. The boilerplate, the dependency arrays, the constant battle with the compiler—it all seemed unnecessarily complex. Vue's intuitive reactivity system just clicked with my mental model of how code should work. And when I discovered Svelte, with its elegant runes system, I found something even better.&lt;/p&gt;

&lt;p&gt;But here's the thing: React isn't going anywhere. It's become the de facto standard for frontend development, backed by Meta and boasting the largest ecosystem of tools, libraries, and job opportunities. Theo explains this dynamic well in his video:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/P1FLEnKZTAE"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;While I've grown more comfortable with React's patterns, I still find myself longing for Svelte's simplicity. There's something beautiful about how Svelte lets you write exactly what you mean, while React often feels like you're negotiating with the framework about your intentions.&lt;/p&gt;

&lt;p&gt;So I did what any stubborn developer would do: instead of switching frameworks, I decided to bring Svelte's elegance to React. This is that experiment.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Svelte 5-Inspired Reactivity for Next.js Apps&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Traditional React state management often involves hooks like &lt;code&gt;useState&lt;/code&gt; and &lt;code&gt;useEffect&lt;/code&gt;, leading to explicit dependency arrays and sometimes verbose code. While powerful, this can obscure the flow of reactivity and lead to subtle bugs if dependencies are missed.&lt;/p&gt;

&lt;p&gt;React Runes aims to simplify this by introducing a "runes" system—familiar to Svelte developers—that provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity:&lt;/strong&gt; A minimal API surface that's easy to grasp.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ergonomics:&lt;/strong&gt; Reactive values in your components with a single, intuitive &lt;code&gt;$&lt;/code&gt; hook.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; Built on fine-grained reactivity principles, reducing unnecessary re-renders.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clarity:&lt;/strong&gt; Makes it explicit which values in your components are reactive.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;How It Works: The Core Runes&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;React Runes provides three fundamental primitives: &lt;code&gt;state&lt;/code&gt;, &lt;code&gt;derived&lt;/code&gt;, and &lt;code&gt;effect&lt;/code&gt;, along with a powerful &lt;code&gt;$&lt;/code&gt; hook.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;state(initial)&lt;/code&gt;&lt;/strong&gt;: Creates a reactive state variable. When its value changes, anything depending on it automatically updates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;derived(fn)&lt;/code&gt;&lt;/strong&gt;: Defines a computed value. This function automatically re-runs whenever any of its dependencies (other runes) change, and its value is then updated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;effect(fn)&lt;/code&gt;&lt;/strong&gt;: Executes a side effect. The &lt;code&gt;effect&lt;/code&gt; function runs whenever its dependencies change, and it can optionally return a cleanup function.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And then, there's the star of the show for React components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;$(rune)&lt;/code&gt; (The &lt;code&gt;$&lt;/code&gt; Hook)&lt;/strong&gt;: This Svelte-inspired hook is how you consume reactive runes directly within your React components. When you wrap a &lt;code&gt;state&lt;/code&gt; or &lt;code&gt;derived&lt;/code&gt; rune with &lt;code&gt;$(...)&lt;/code&gt;, your component automatically subscribes to its value and re-renders only when that specific rune's value changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s a quick example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;derived&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-runes&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;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&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;doubleCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;derived&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;effect&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Count changed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;count&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Counter&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;countValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&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;doubleValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;doubleCount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Count: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;countValue&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Double: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;doubleValue&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Increment&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Notice how &lt;code&gt;count.value&lt;/code&gt; and &lt;code&gt;doubleCount.value&lt;/code&gt; are directly accessed within the &lt;code&gt;derived&lt;/code&gt; and &lt;code&gt;effect&lt;/code&gt;, and how &lt;code&gt;$(count)&lt;/code&gt; and &lt;code&gt;$(doubleCount)&lt;/code&gt; keep the &lt;code&gt;Counter&lt;/code&gt; component reactive without explicit &lt;code&gt;useState&lt;/code&gt; or &lt;code&gt;useEffect&lt;/code&gt; calls for subscription.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Under the Hood&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8u94oiptgnarg973iun.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8u94oiptgnarg973iun.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;React Runes leverages &lt;code&gt;zustand&lt;/code&gt; for its global state management. This allows for an efficient, centralized system to track all runes, manage their dependencies, and batch updates for optimal performance. When a &lt;code&gt;state&lt;/code&gt; rune is updated, the system intelligently identifies and re-runs only the &lt;code&gt;derived&lt;/code&gt; and &lt;code&gt;effect&lt;/code&gt; runes that depend on it, propagating changes efficiently.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Vertical Slice Architecture&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;One of the philosophies behind React Runes is to encourage a "vertical slice" architecture. This means co-locating your component's UI, its reactive state (runes), and related logic within the same directory. This enhances discoverability and makes features more self-contained.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Limitations and Future&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;It's important to reiterate that React Runes is an &lt;strong&gt;experiment&lt;/strong&gt; and not intended for production use. It pushes the boundaries of React ergonomics and explores alternative reactivity patterns. As such, some React tooling (like linting or auto-imports) may not fully recognize &lt;code&gt;$&lt;/code&gt; as a conventional hook.&lt;/p&gt;

&lt;p&gt;This project is a playground for reactivity ideas within the React ecosystem. I invite you to explore the codebase, try it out, and share your feedback. Contributions, ideas, and discussions are always welcome.&lt;/p&gt;




&lt;p&gt;Source Code: &lt;a href="https://github.com/trentbrew/react-runes" rel="noopener noreferrer"&gt;https://github.com/trentbrew/react-runes&lt;/a&gt;&lt;br&gt;
NPM Package: &lt;a href="https://www.npmjs.com/package/react-runes" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/react-runes&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Deploy PocketBase in 30 Seconds</title>
      <dc:creator>Trent Brew</dc:creator>
      <pubDate>Mon, 20 Mar 2023 00:42:44 +0000</pubDate>
      <link>https://dev.to/trentbrew/deploy-pocketbase-in-30-seconds-57he</link>
      <guid>https://dev.to/trentbrew/deploy-pocketbase-in-30-seconds-57he</guid>
      <description>&lt;p&gt;Just wanted to share this amazing tool called &lt;a href="https://pockethost.io/" rel="noopener noreferrer"&gt;pockethost.io&lt;/a&gt;. It allows you to get your PocketBase instance up &amp;amp; running quickly without having to fiddle with Docker or provision your server. You just pick a unique name for your app and you're good to go with the click of a button 🚀&lt;/p&gt;

&lt;p&gt;Big shout out to &lt;a href="https://github.com/benallfree" rel="noopener noreferrer"&gt;Ben&lt;/a&gt;, &lt;a href="https://github.com/shaparder" rel="noopener noreferrer"&gt;Oscar&lt;/a&gt;, &lt;a href="https://github.com/Ianmello10" rel="noopener noreferrer"&gt;Lucas&lt;/a&gt;, and &lt;a href="https://github.com/brewhousedigital" rel="noopener noreferrer"&gt;Zach&lt;/a&gt; 💙 &lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/benallfree/pockethost" rel="noopener noreferrer"&gt;github.com/pockethost&lt;/a&gt;&lt;/p&gt;

</description>
      <category>pocketbase</category>
      <category>backend</category>
      <category>baas</category>
      <category>deploy</category>
    </item>
    <item>
      <title>Using Notion as a Headless CMS with Nuxt</title>
      <dc:creator>Trent Brew</dc:creator>
      <pubDate>Fri, 06 Jan 2023 20:58:38 +0000</pubDate>
      <link>https://dev.to/trentbrew/using-notion-as-a-headless-cms-with-nuxt-3mk</link>
      <guid>https://dev.to/trentbrew/using-notion-as-a-headless-cms-with-nuxt-3mk</guid>
      <description>&lt;p&gt;Notion has become increasingly popular as a versatile tool for everything from taking notes to managing projects, and when combined with it's API we're able to automate workflows, create custom apps, and even use Notion as a headless CMS for websites and blogs.&lt;/p&gt;

&lt;p&gt;With the help of tools like Nuxt, we can easily create dynamic websites and blogs that are powered by Notion. In this tutorial, we will explore how to use Notion as a headless CMS with Nuxt 3. We will cover setting up the Notion API, creating a database of images in Notion, and building a simple website that displays our content.&lt;/p&gt;

&lt;p&gt;By the end of this tutorial, you should have a good understanding of how to use Notion as a headless CMS with Nuxt 3.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 1: Project Setup
&lt;/h1&gt;

&lt;p&gt;Create a new Nuxt project and install dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nuxi init notion-cms &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;notion-cms &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F25etmxdc0t0x3e72cdwq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F25etmxdc0t0x3e72cdwq.png" alt=" " width="800" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From here, we can begin configuring notion.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 2: Create Notion Database
&lt;/h1&gt;

&lt;p&gt;Create a new Notion page. This page will house our image database:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8866oqefvu4fb9yp39r5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8866oqefvu4fb9yp39r5.png" alt=" " width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add an inline database called 'Images' using the gallery layout:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkh5rcglbuo08p4bdf3yi.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkh5rcglbuo08p4bdf3yi.gif" alt=" " width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we'll create a 'Files &amp;amp; media' property to upload an image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fym7u2tmq2a04c685qf8l.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fym7u2tmq2a04c685qf8l.gif" alt=" " width="760" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can set the gallery preview thumbnail to display the uploaded file:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0wef1f1kt94kxrq16z1i.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0wef1f1kt94kxrq16z1i.gif" alt=" " width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's upload a few more for good measure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fue4dvwopzbhyp23tta02.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fue4dvwopzbhyp23tta02.png" alt=" " width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our database it looking great! However, we need to hook up the database to an integration before we can access it from the API.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 3: Create a new Notion Integration
&lt;/h1&gt;

&lt;p&gt;Visit &lt;a href="https://www.notion.so/my-integrations" rel="noopener noreferrer"&gt;notion.so/my-integrations&lt;/a&gt; to create a new integration.&lt;/p&gt;

&lt;p&gt;Once it's created, an 'Internal Integration Token' will be generated:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1o8s5nqlfi0ismb3p4ke.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1o8s5nqlfi0ismb3p4ke.png" alt=" " width="504" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll need this token later on to access the database from the client, so we can copy and paste it somewhere secure for now.&lt;/p&gt;

&lt;p&gt;I've named my integration 'CMS' with a custom thumbnail:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F21ejx0e9jzsa7gmk06b2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F21ejx0e9jzsa7gmk06b2.png" alt=" " width="516" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set integration type as 'internal':&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frppgkeb7y7dota74a1pq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frppgkeb7y7dota74a1pq.png" alt=" " width="502" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since we only want to fetch images and not update or delete anything from the client, we'll just check 'Read Content':&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmxnig126i7ctqstn8qnw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmxnig126i7ctqstn8qnw.png" alt=" " width="502" height="747"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 4: Connect Integration to Database
&lt;/h1&gt;

&lt;p&gt;Head back to the database page we created in Step 2.&lt;/p&gt;

&lt;p&gt;From here, we can click 'Add connections' where we'll find the 'CMS' integration we created in Step 3:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxif3j8pohz5nsl1pdnk3.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxif3j8pohz5nsl1pdnk3.gif" alt=" " width="760" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With that, we're all set on the Notion side. Let's switch gears and jump into the code.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 5: Setup Environment Variables
&lt;/h1&gt;

&lt;p&gt;This is where our 'Internal Integration Token' from Step 3 comes in: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1o8s5nqlfi0ismb3p4ke.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1o8s5nqlfi0ismb3p4ke.png" alt=" " width="504" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the root of your nuxt project, create a &lt;code&gt;.env&lt;/code&gt; file. This file will contain our internal integration token and the ID of the database we created in Step 2.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F74bhxj1gypufisxgnc5d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F74bhxj1gypufisxgnc5d.png" alt=" " width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The ID of the database can be found in the URL:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1xewn7cl15uh1zzbhhb9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1xewn7cl15uh1zzbhhb9.gif" alt=" " width="759" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;NOTION_API_KEY&lt;/code&gt; should be set as the secret internal integration key.&lt;/p&gt;

&lt;p&gt;Your &lt;code&gt;.env&lt;/code&gt; file should look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3nbuxofkupi6huj6ih1b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3nbuxofkupi6huj6ih1b.png" alt=" " width="799" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 6: Nuxt Server Setup
&lt;/h1&gt;

&lt;p&gt;Create a new file &lt;code&gt;server/api/gallery.get.js&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fat4twk3ce5rst6adyk52.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fat4twk3ce5rst6adyk52.png" alt=" " width="800" height="244"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The handler in this file will be called when the user hits the endpoint &lt;code&gt;localhost:3000/api/gallery&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Let's return the following dummy data to check if it's working:&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="c1"&gt;// gallery.get.js&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;test_data&lt;/span&gt; &lt;span class="o"&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;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;item1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;item2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;item3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineEventHandler&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;test_data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We should be able to see the JSON response in the browser now 🙌🏾&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp9fuqmz4c9fe7pmh9o98.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp9fuqmz4c9fe7pmh9o98.png" alt=" " width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From here, we can install the Notion client and begin making requests.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 7: Notion API Client Setup
&lt;/h1&gt;

&lt;p&gt;Install the notion client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @notionhq/client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's pull in the environment variables for authorization and try fetching the database matching the ID in our &lt;code&gt;.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// gallery.get.js&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@notionhq/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;notion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NOTION_API_KEY&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;image_database_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NOTION_DATABASE_ID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getImages&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;data&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;notion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;databases&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;database_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;image_database_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;getImages&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineEventHandler&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when we visit &lt;code&gt;localhost:3000/api/gallery&lt;/code&gt;, we can see the data returned from our Notion database ＼(￣▽￣)／&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcgshfexmqtxk7jxhi5i9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcgshfexmqtxk7jxhi5i9.png" alt=" " width="800" height="1166"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We don't need quite so much data though... we only care about the image url in this case, so let's isolate the data we need before sending back to the front-end:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdcd1nrtslmuy24p6u1c2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdcd1nrtslmuy24p6u1c2.png" alt=" " width="800" height="678"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we only receive the neccesary data from Notion:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1y5ow2hc6rbspz8dds6o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1y5ow2hc6rbspz8dds6o.png" alt=" " width="800" height="851"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 8: Render the Images
&lt;/h1&gt;

&lt;p&gt;Finally, we can fetch the image URLs from the server and render them on the front-end ~ let's add a bit of styling as well:&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="c1"&gt;// app.vue&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="nx"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;reactive&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;images&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:3000/api/gallery&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;images&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;ヽ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;♡‿♡&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nx"&gt;ノ&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;(image, index) in state.images&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;index&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/template&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;&amp;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;font&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;;&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="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;font&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;monospace&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;margin&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;margin&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cover&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;border&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="nx"&gt;ms&lt;/span&gt; &lt;span class="nx"&gt;cubic&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nf"&gt;bezier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nl"&gt;img&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="nf"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="nx"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;align&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;justify&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="nx"&gt;vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="nx"&gt;vw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;linear&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nf"&gt;gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;black&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;111&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/style&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now see the images rendered on the front-end:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm9r7leyf9dmd2mvwq2we.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm9r7leyf9dmd2mvwq2we.gif" alt=" " width="720" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion:
&lt;/h1&gt;

&lt;p&gt;Notion is a powerful tool and works surprisingly well as a headless CMS.&lt;/p&gt;

&lt;p&gt;My &lt;a href="https://trentbrew.com" rel="noopener noreferrer"&gt;personal website&lt;/a&gt; is powered by Notion, and while there are some trade-offs, Notion has become my goto for projects requiring a light weight CMS.&lt;/p&gt;

&lt;p&gt;I would love to try some alternatives so please let me know your favorite CMS for small-scale projects!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/trentbrew/notion-cms-tutorial" rel="noopener noreferrer"&gt;Full Source Code&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  References
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developers.notion.com/reference/intro" rel="noopener noreferrer"&gt;Notion API Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nuxt.com/docs/guide/directory-structure/server" rel="noopener noreferrer"&gt;Nuxt Server Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>nuxt</category>
      <category>vue</category>
      <category>cms</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
