DEV Community: Trent Brew The latest articles on DEV Community by Trent Brew (@trentbrew). https://dev.to/trentbrew https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F881231%2F39c385ed-a2b6-4f90-98f6-926a029129b1.png DEV Community: Trent Brew https://dev.to/trentbrew en What I Learned Building a Knowledge Graph for AI Agents Trent Brew Fri, 07 Nov 2025 22:51:04 +0000 https://dev.to/trentbrew/what-i-learned-building-a-knowledge-graph-for-ai-agents-3e65 https://dev.to/trentbrew/what-i-learned-building-a-knowledge-graph-for-ai-agents-3e65 <p><a href="proxy.php?url=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"><img src="proxy.php?url=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="337"></a></p> <p>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.</p> <p>The fix: let agents query project knowledge like a database instead of parsing human prose.</p> <p><strong>Before: Context scattered across files</strong><br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="c"># “What’s blocking the auth feature?”</span> <span class="c"># AI scrapes TODO.md, commit messages, Slack. Guesses. You verify manually.</span> </code></pre> </div> <p><strong>After: Query the graph</strong><br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code>code-hq query <span class="s1">'FIND ?b WHERE urn:task:auth-123 dependsOn* ?b AND ?b.taskStatus != "Done"'</span> <span class="c"># → urn:task:db-456 (Setup database, assigned to Bob)</span> </code></pre> </div> <h1> The core problems </h1> <ul> <li> <strong>Missing relationships</strong>: TODO lists describe tasks in isolation. Real work is about dependencies, ownership, and ripple effects.</li> <li> <strong>Context brittleness</strong>: Rephrase a comment or move a task, and the AI's understanding breaks. No stable way to reference project state.</li> <li> <strong>Translation overhead</strong>: Humans use Markdown. Agents need structured data.</li> </ul> <p>Solution: maintain two layers - one for humans (Markdown, UIs) and one for machines (structured graph). Keep them synchronized.</p> <h1> The approach </h1> <h2> 1) Everything as a graph </h2> <p>Projects aren't lists - they're webs of relationships. Represent everything as JSON-LD entities with clear connections:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight json"><code><span class="p">{</span><span class="w"> </span><span class="nl">"@context"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://schema.org"</span><span class="p">,</span><span class="w"> </span><span class="nl">"@graph"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"@id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"urn:task:auth-123"</span><span class="p">,</span><span class="w"> </span><span class="nl">"@type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Task"</span><span class="p">,</span><span class="w"> </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Fix authentication bug"</span><span class="p">,</span><span class="w"> </span><span class="nl">"taskStatus"</span><span class="p">:</span><span class="w"> </span><span class="s2">"InProgress"</span><span class="p">,</span><span class="w"> </span><span class="nl">"priority"</span><span class="p">:</span><span class="w"> </span><span class="s2">"high"</span><span class="p">,</span><span class="w"> </span><span class="nl">"assignee"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"@id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"urn:person:alice"</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nl">"dependsOn"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="w"> </span><span class="nl">"@id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"urn:task:db-456"</span><span class="w"> </span><span class="p">}],</span><span class="w"> </span><span class="nl">"dateModified"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2025-10-24T04:12:00Z"</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"@id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"urn:task:db-456"</span><span class="p">,</span><span class="w"> </span><span class="nl">"@type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Task"</span><span class="p">,</span><span class="w"> </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Setup database"</span><span class="p">,</span><span class="w"> </span><span class="nl">"taskStatus"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Blocked"</span><span class="p">,</span><span class="w"> </span><span class="nl">"assignee"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"@id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"urn:person:bob"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre> </div> <h2> 2) A query language for exploring relationships </h2> <p>A simple query language that traverses relationships and filters results:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>FIND ?t WHERE ?t a Task ; dependsOn* ?b . ?b taskStatus != "Done" . FILTER (?t = urn:task:auth-123) </code></pre> </div> <h2> 3) CLI for humans </h2> <p>A CLI that handles the tedious parts:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code>code-hq init code-hq create task <span class="s2">"Fix auth bug"</span> <span class="nt">--priority</span> high <span class="nt">--assignee</span> trent code-hq tasks <span class="nt">--status</span> blocked code-hq people <span class="nt">--role</span> developer code-hq show <span class="nt">--view</span> kanban </code></pre> </div> <p><a href="proxy.php?url=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"><img src="proxy.php?url=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="760" height="393"></a></p> <h2> 4) Generated views </h2> <p>Generate human-readable views from the structured data:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight markdown"><code><span class="gh"># Tasks (Generated)</span> <span class="gu">## In Progress</span> <span class="p">-</span> Fix authentication bug (@trent, high) <span class="gu">## Blocked</span> <span class="p">-</span> Setup database (@trent) </code></pre> </div> <p>Humans use Markdown. Agents get structured data. Both stay synchronized through the CLI.</p> <h1> Migration </h1> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="c"># Parse TODO.md → tasks with inferred priority/assignee/dependencies</span> code-hq import todo ./TODO.md <span class="c"># Import GitHub issues (PLACEHOLDER: add repo)</span> code-hq import github <span class="nt">--repo</span> org/repo <span class="c"># Keep files in sync (graph → Markdown views)</span> code-hq render <span class="nt">--views</span> kanban,timeline </code></pre> </div> <h1> Trade-offs </h1> <ul> <li> <strong>Authoring cost</strong> → CLI verbs (<code>create</code>, <code>link</code>, <code>set</code>) and smart importers make structured data feel natural</li> <li> <strong>Schema drift</strong> → JSON-LD contexts with validation on write</li> <li> <strong>Review friction</strong> → Small, machine-stable diffs in <code>.code-hq/</code> </li> <li> <strong>Team adoption</strong> → Humans keep Markdown, agents get structure</li> <li> <strong>VSCode only</strong> → Visualization layer depends on VSCode extension for now (web client coming soon): <a href="proxy.php?url=https://github.com/trentbrew/code-hq-vscode" rel="noopener noreferrer">code-hq-vscode</a> </li> </ul> <h1> Getting started </h1> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="c"># Start with a simple graph</span> code-hq init <span class="c"># Add a few tasks with relationships</span> code-hq create task <span class="s2">"Test semantic workflows"</span> <span class="nt">--priority</span> medium <span class="c"># Query to understand the current state</span> code-hq query <span class="s1">'FIND ?t WHERE ?t a Task ; priority "high"'</span> <span class="c"># See it from a human perspective</span> code-hq show <span class="nt">--view</span> kanban </code></pre> </div> <h1> Architecture </h1> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>┌─────────────────────────┐ │ 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 └─────────────────────────┘ </code></pre> </div> <h1> IDE Integration </h1> <h3> Cursor </h3> <p>Add to <code>.cursorrules</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight markdown"><code>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. </code></pre> </div> <h3> Windsurf </h3> <p>Create <code>.windsurf/workflows/codehq.md</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight markdown"><code><span class="nn">---</span> <span class="na">description</span><span class="pi">:</span> <span class="s">code-hq commands reference</span> <span class="nn">---</span> Read <span class="sb">`.code-hq/prompts/_index.md`</span> for overview. See <span class="sb">`.code-hq/prompts/task-management.md`</span> for details. </code></pre> </div> <h3> Claude Code </h3> <p>Automatically sees <code>.code-hq/prompts/</code> as context.<br> Ask: "How do I manage tasks in this project?"</p> <h1> What's next </h1> <p>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.</p> <h3> CodeHQ: The CLI </h3> <blockquote> <p>Source Code: <a href="proxy.php?url=https://github.com/trentbrew/code-hq" rel="noopener noreferrer">https://github.com/trentbrew/code-hq</a><br> NPM Package: <a href="proxy.php?url=https://www.npmjs.com/package/code-hq" rel="noopener noreferrer">https://www.npmjs.com/package/code-hq</a></p> </blockquote> <h3> CodeHQ: The VSCode Extension </h3> <blockquote> <p><a href="proxy.php?url=https://open-vsx.org/extension/codehq/codehq-vscode" rel="noopener noreferrer">https://open-vsx.org/extension/codehq/codehq-vscode</a></p> </blockquote> <h3> TQL: The Query Language </h3> <blockquote> <p>Source Code: <a href="proxy.php?url=https://github.com/trentbrew/TQL" rel="noopener noreferrer">https://github.com/trentbrew/TQL</a></p> </blockquote> <h3> JSON-LD: JSON For Linked Data </h3> <blockquote> <p>Docs: <a href="proxy.php?url=https://json-ld.org/" rel="noopener noreferrer">https://json-ld.org/</a></p> </blockquote> programming ai productivity vscode I didn't like React so I made it more like Svelte Trent Brew Sat, 07 Jun 2025 21:29:21 +0000 https://dev.to/trentbrew/using-svelte-runes-in-react-3g48 https://dev.to/trentbrew/using-svelte-runes-in-react-3g48 <p>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.</p> <p>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:</p> <p><iframe width="710" height="399" src="proxy.php?url=https://www.youtube.com/embed/P1FLEnKZTAE"> </iframe> </p> <p>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.</p> <p>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.</p> <h3> <strong>Svelte 5-Inspired Reactivity for Next.js Apps</strong> </h3> <p>Traditional React state management often involves hooks like <code>useState</code> and <code>useEffect</code>, 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.</p> <p>React Runes aims to simplify this by introducing a "runes" system—familiar to Svelte developers—that provides:</p> <ul> <li> <strong>Simplicity:</strong> A minimal API surface that's easy to grasp.</li> <li> <strong>Ergonomics:</strong> Reactive values in your components with a single, intuitive <code>$</code> hook.</li> <li> <strong>Performance:</strong> Built on fine-grained reactivity principles, reducing unnecessary re-renders.</li> <li> <strong>Clarity:</strong> Makes it explicit which values in your components are reactive.</li> </ul> <h3> <strong>How It Works: The Core Runes</strong> </h3> <p>React Runes provides three fundamental primitives: <code>state</code>, <code>derived</code>, and <code>effect</code>, along with a powerful <code>$</code> hook.</p> <ul> <li> <strong><code>state(initial)</code></strong>: Creates a reactive state variable. When its value changes, anything depending on it automatically updates.</li> <li> <strong><code>derived(fn)</code></strong>: Defines a computed value. This function automatically re-runs whenever any of its dependencies (other runes) change, and its value is then updated.</li> <li> <strong><code>effect(fn)</code></strong>: Executes a side effect. The <code>effect</code> function runs whenever its dependencies change, and it can optionally return a cleanup function.</li> </ul> <p>And then, there's the star of the show for React components:</p> <ul> <li> <strong><code>$(rune)</code> (The <code>$</code> Hook)</strong>: This Svelte-inspired hook is how you consume reactive runes directly within your React components. When you wrap a <code>state</code> or <code>derived</code> rune with <code>$(...)</code>, your component automatically subscribes to its value and re-renders only when that specific rune's value changes.</li> </ul> <p>Here’s a quick example:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight tsx"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">state</span><span class="p">,</span> <span class="nx">derived</span><span class="p">,</span> <span class="nx">effect</span><span class="p">,</span> <span class="nx">$</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">react-runes</span><span class="dl">'</span> <span class="kd">const</span> <span class="nx">count</span> <span class="o">=</span> <span class="nf">state</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="kd">const</span> <span class="nx">doubleCount</span> <span class="o">=</span> <span class="nf">derived</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="nx">count</span><span class="p">.</span><span class="nx">value</span> <span class="o">*</span> <span class="mi">2</span><span class="p">)</span> <span class="nf">effect</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">Count changed:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">count</span><span class="p">.</span><span class="nx">value</span><span class="p">)</span> <span class="p">})</span> <span class="k">export</span> <span class="k">default</span> <span class="kd">function</span> <span class="nf">Counter</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">countValue</span> <span class="o">=</span> <span class="nf">$</span><span class="p">(</span><span class="nx">count</span><span class="p">)</span> <span class="kd">const</span> <span class="nx">doubleValue</span> <span class="o">=</span> <span class="nf">$</span><span class="p">(</span><span class="nx">doubleCount</span><span class="p">)</span> <span class="k">return </span><span class="p">(</span> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>Count: <span class="si">{</span><span class="nx">countValue</span><span class="si">}</span><span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>Double: <span class="si">{</span><span class="nx">doubleValue</span><span class="si">}</span><span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">button</span> <span class="na">onClick</span><span class="p">=</span><span class="si">{</span><span class="p">()</span> <span class="o">=&gt;</span> <span class="p">(</span><span class="nx">count</span><span class="p">.</span><span class="nx">value</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">)</span><span class="si">}</span><span class="p">&gt;</span>Increment<span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> <span class="p">)</span> <span class="p">}</span> </code></pre> </div> <p>Notice how <code>count.value</code> and <code>doubleCount.value</code> are directly accessed within the <code>derived</code> and <code>effect</code>, and how <code>$(count)</code> and <code>$(doubleCount)</code> keep the <code>Counter</code> component reactive without explicit <code>useState</code> or <code>useEffect</code> calls for subscription.</p> <h3> <strong>Under the Hood</strong> </h3> <p><a href="proxy.php?url=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"><img src="proxy.php?url=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="Image description" width="800" height="455"></a></p> <p>React Runes leverages <code>zustand</code> 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 <code>state</code> rune is updated, the system intelligently identifies and re-runs only the <code>derived</code> and <code>effect</code> runes that depend on it, propagating changes efficiently.</p> <h3> <strong>Vertical Slice Architecture</strong> </h3> <p>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.</p> <h3> <strong>Limitations and Future</strong> </h3> <p>It's important to reiterate that React Runes is an <strong>experiment</strong> 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 <code>$</code> as a conventional hook.</p> <p>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.</p> <p>Source Code: <a href="proxy.php?url=https://github.com/trentbrew/react-runes" rel="noopener noreferrer">https://github.com/trentbrew/react-runes</a><br> NPM Package: <a href="proxy.php?url=https://www.npmjs.com/package/react-runes" rel="noopener noreferrer">https://www.npmjs.com/package/react-runes</a></p> Deploy PocketBase in 30 Seconds Trent Brew Mon, 20 Mar 2023 00:42:44 +0000 https://dev.to/trentbrew/deploy-pocketbase-in-30-seconds-57he https://dev.to/trentbrew/deploy-pocketbase-in-30-seconds-57he <p>Just wanted to share this amazing tool called <a href="proxy.php?url=https://pockethost.io/" rel="noopener noreferrer">pockethost.io</a>. It allows you to get your PocketBase instance up &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 🚀</p> <p>Big shout out to <a href="proxy.php?url=https://github.com/benallfree" rel="noopener noreferrer">Ben</a>, <a href="proxy.php?url=https://github.com/shaparder" rel="noopener noreferrer">Oscar</a>, <a href="proxy.php?url=https://github.com/Ianmello10" rel="noopener noreferrer">Lucas</a>, and <a href="proxy.php?url=https://github.com/brewhousedigital" rel="noopener noreferrer">Zach</a> 💙 </p> <p>GitHub: <a href="proxy.php?url=https://github.com/benallfree/pockethost" rel="noopener noreferrer">github.com/pockethost</a></p> pocketbase backend baas deploy Using Notion as a Headless CMS with Nuxt Trent Brew Fri, 06 Jan 2023 20:58:38 +0000 https://dev.to/trentbrew/using-notion-as-a-headless-cms-with-nuxt-3mk https://dev.to/trentbrew/using-notion-as-a-headless-cms-with-nuxt-3mk <p>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.</p> <p>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.</p> <p>By the end of this tutorial, you should have a good understanding of how to use Notion as a headless CMS with Nuxt 3.</p> <h1> Step 1: Project Setup </h1> <p>Create a new Nuxt project and install dependencies:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code>npx nuxi init notion-cms <span class="nt">-y</span> <span class="o">&amp;&amp;</span> <span class="nb">cd </span>notion-cms <span class="o">&amp;&amp;</span> npm <span class="nb">install</span> </code></pre> </div> <p>Run locally:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code>npm run dev </code></pre> </div> <p><a href="proxy.php?url=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"><img src="proxy.php?url=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="Image description" width="800" height="423"></a></p> <p>From here, we can begin configuring notion.</p> <h1> Step 2: Create Notion Database </h1> <p>Create a new Notion page. This page will house our image database:</p> <p><a href="proxy.php?url=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"><img src="proxy.php?url=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="Image description" width="800" height="408"></a></p> <p>Add an inline database called 'Images' using the gallery layout:</p> <p><a href="proxy.php?url=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"><img src="proxy.php?url=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="Image description" width="800" height="408"></a></p> <p>Next, we'll create a 'Files &amp; media' property to upload an image:</p> <p><a href="proxy.php?url=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"><img src="proxy.php?url=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="Image description" width="800" height="400"></a></p> <p>We can set the gallery preview thumbnail to display the uploaded file:</p> <p><a href="proxy.php?url=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"><img src="proxy.php?url=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="Image description" width="800" height="408"></a></p> <p>Let's upload a few more for good measure:</p> <p><a href="proxy.php?url=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"><img src="proxy.php?url=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="Image description" width="800" height="408"></a></p> <p>Our database it looking great! However, we need to hook up the database to an integration before we can access it from the API.</p> <h1> Step 3: Create a new Notion Integration </h1> <p>Visit <a href="proxy.php?url=https://www.notion.so/my-integrations" rel="noopener noreferrer">notion.so/my-integrations</a> to create a new integration.</p> <p>Once it's created, an 'Internal Integration Token' will be generated:</p> <p><a href="proxy.php?url=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"><img src="proxy.php?url=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="Image description" width="504" height="237"></a></p> <p>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.</p> <p>I've named my integration 'CMS' with a custom thumbnail:</p> <p><a href="proxy.php?url=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"><img src="proxy.php?url=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="Image description" width="516" height="426"></a></p> <p>Set integration type as 'internal':</p> <p><a href="proxy.php?url=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"><img src="proxy.php?url=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="Image description" width="502" height="290"></a></p> <p>Since we only want to fetch images and not update or delete anything from the client, we'll just check 'Read Content':</p> <p><a href="proxy.php?url=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"><img src="proxy.php?url=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="Image description" width="502" height="747"></a></p> <h1> Step 4: Connect Integration to Database </h1> <p>Head back to the database page we created in Step 2.</p> <p>From here, we can click 'Add connections' where we'll find the 'CMS' integration we created in Step 3:</p> <p><a href="proxy.php?url=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"><img src="proxy.php?url=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="Image description" width="800" height="408"></a></p> <p>With that, we're all set on the Notion side. Let's switch gears and jump into the code.</p> <h1> Step 5: Setup Environment Variables </h1> <p>This is where our 'Internal Integration Token' from Step 3 comes in: </p> <p><a href="proxy.php?url=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"><img src="proxy.php?url=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="Image description" width="504" height="237"></a></p> <p>In the root of your nuxt project, create a <code>.env</code> file. This file will contain our internal integration token and the ID of the database we created in Step 2.</p> <p><a href="proxy.php?url=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"><img src="proxy.php?url=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="Image description" width="800" height="364"></a></p> <p>The ID of the database can be found in the URL:</p> <p><a href="proxy.php?url=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"><img src="proxy.php?url=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="Image description" width="800" height="470"></a></p> <p><code>NOTION_API_KEY</code> should be set as the secret internal integration key.</p> <p>Your <code>.env</code> file should look something like this:</p> <p><a href="proxy.php?url=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"><img src="proxy.php?url=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="Image description" width="800" height="359"></a></p> <h1> Step 6: Nuxt Server Setup </h1> <p>Create a new file <code>server/api/gallery.get.js</code>:</p> <p><a href="proxy.php?url=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"><img src="proxy.php?url=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="Image description" width="800" height="243"></a></p> <p>The handler in this file will be called when the user hits the endpoint <code>localhost:3000/api/gallery</code></p> <p>Let's return the following dummy data to check if it's working:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code><span class="c1">// gallery.get.js</span> <span class="kd">const</span> <span class="nx">test_data</span> <span class="o">=</span> <span class="p">[</span> <span class="p">{</span> <span class="na">id</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">item1</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">{</span> <span class="na">id</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">item2</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">{</span> <span class="na">id</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span> <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">item3</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">];</span> <span class="k">export</span> <span class="k">default</span> <span class="nf">defineEventHandler</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="nx">test_data</span><span class="p">);</span> </code></pre> </div> <p>We should be able to see the JSON response in the browser now 🙌🏾</p> <p><a href="proxy.php?url=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"><img src="proxy.php?url=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="Image description" width="800" height="470"></a></p> <p>From here, we can install the Notion client and begin making requests.</p> <h1> Step 7: Notion API Client Setup </h1> <p>Install the notion client:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code>npm <span class="nb">install</span> @notionhq/client </code></pre> </div> <p>Let's pull in the environment variables for authorization and try fetching the database matching the ID in our <code>.env</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code><span class="c1">// gallery.get.js</span> <span class="k">import</span> <span class="p">{</span> <span class="nx">Client</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">@notionhq/client</span><span class="dl">"</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">notion</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Client</span><span class="p">({</span> <span class="na">auth</span><span class="p">:</span> <span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">NOTION_API_KEY</span> <span class="p">});</span> <span class="kd">const</span> <span class="nx">image_database_id</span> <span class="o">=</span> <span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">NOTION_DATABASE_ID</span><span class="p">;</span> <span class="kd">let</span> <span class="nx">payload</span> <span class="o">=</span> <span class="p">[];</span> <span class="k">async</span> <span class="kd">function</span> <span class="nf">getImages</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">notion</span><span class="p">.</span><span class="nx">databases</span><span class="p">.</span><span class="nf">query</span><span class="p">({</span> <span class="na">database_id</span><span class="p">:</span> <span class="nx">image_database_id</span><span class="p">,</span> <span class="p">});</span> <span class="k">return</span> <span class="nx">data</span><span class="p">;</span> <span class="p">}</span> <span class="nf">getImages</span><span class="p">().</span><span class="nf">then</span><span class="p">((</span><span class="nx">data</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">payload</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">results</span><span class="p">;</span> <span class="p">});</span> <span class="k">export</span> <span class="k">default</span> <span class="nf">defineEventHandler</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="nx">payload</span><span class="p">);</span> </code></pre> </div> <p>Now when we visit <code>localhost:3000/api/gallery</code>, we can see the data returned from our Notion database \( ̄▽ ̄)/</p> <p><a href="proxy.php?url=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"><img src="proxy.php?url=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="Image description" width="800" height="1166"></a></p> <p>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:</p> <p><a href="proxy.php?url=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"><img src="proxy.php?url=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="Image description" width="800" height="678"></a></p> <p>Now we only receive the neccesary data from Notion:</p> <p><a href="proxy.php?url=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"><img src="proxy.php?url=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="Image description" width="800" height="850"></a></p> <h1> Step 8: Render the Images </h1> <p>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:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code><span class="c1">// app.vue</span> <span class="o">&lt;</span><span class="nx">script</span> <span class="nx">setup</span><span class="o">&gt;</span> <span class="kd">const</span> <span class="nx">state</span> <span class="o">=</span> <span class="nf">reactive</span><span class="p">({</span> <span class="na">images</span><span class="p">:</span> <span class="p">[],</span> <span class="p">});</span> <span class="kd">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">fetch</span><span class="p">(</span><span class="dl">"</span><span class="s2">http://localhost:3000/api/gallery</span><span class="dl">"</span><span class="p">);</span> <span class="nx">res</span><span class="p">.</span><span class="nf">json</span><span class="p">().</span><span class="nf">then</span><span class="p">((</span><span class="nx">images</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="nx">images</span><span class="p">);</span> <span class="nx">state</span><span class="p">.</span><span class="nx">images</span> <span class="o">=</span> <span class="nx">images</span><span class="p">;</span> <span class="p">});</span> <span class="o">&lt;</span><span class="sr">/script</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="nx">template</span><span class="o">&gt;</span> <span class="o">&lt;</span><span class="nx">main</span><span class="o">&gt;</span> <span class="o">&lt;</span><span class="nx">h1</span><span class="o">&gt;</span><span class="nf">ヽ</span><span class="p">(</span><span class="err">♡‿♡</span><span class="p">)</span><span class="nx">ノ</span><span class="o">&lt;</span><span class="sr">/h1</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="nx">div</span> <span class="nx">v</span><span class="o">-</span><span class="k">for</span><span class="o">=</span><span class="dl">"</span><span class="s2">(image, index) in state.images</span><span class="dl">"</span> <span class="p">:</span><span class="nx">key</span><span class="o">=</span><span class="dl">"</span><span class="s2">index</span><span class="dl">"</span><span class="o">&gt;</span> <span class="o">&lt;</span><span class="nx">img</span> <span class="p">:</span><span class="nx">src</span><span class="o">=</span><span class="dl">"</span><span class="s2">image</span><span class="dl">"</span> <span class="o">/&gt;</span> <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="sr">/main</span><span class="err">&gt; </span><span class="o">&lt;</span><span class="sr">/template</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="nx">style</span><span class="o">&gt;</span> <span class="o">*</span> <span class="p">{</span> <span class="nx">font</span><span class="o">-</span><span class="na">weight</span><span class="p">:</span> <span class="nx">normal</span><span class="p">;</span> <span class="p">}</span> <span class="nx">body</span> <span class="p">{</span> <span class="nl">padding</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span> <span class="nl">margin</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">font</span><span class="o">-</span><span class="nx">family</span><span class="p">:</span> <span class="nx">monospace</span><span class="p">;</span> <span class="nl">color</span><span class="p">:</span> <span class="nx">white</span><span class="p">;</span> <span class="p">}</span> <span class="nx">h1</span> <span class="p">{</span> <span class="nl">position</span><span class="p">:</span> <span class="nx">absolute</span><span class="p">;</span> <span class="nx">margin</span><span class="o">-</span><span class="nx">left</span><span class="p">:</span> <span class="nx">auto</span><span class="p">;</span> <span class="nx">margin</span><span class="o">-</span><span class="nx">right</span><span class="p">:</span> <span class="nx">auto</span><span class="p">;</span> <span class="nl">top</span><span class="p">:</span> <span class="mi">64</span><span class="nx">px</span><span class="p">;</span> <span class="p">}</span> <span class="nx">img</span> <span class="p">{</span> <span class="nl">width</span><span class="p">:</span> <span class="mi">200</span><span class="nx">px</span><span class="p">;</span> <span class="nl">height</span><span class="p">:</span> <span class="mi">200</span><span class="nx">px</span><span class="p">;</span> <span class="nx">object</span><span class="o">-</span><span class="nx">fit</span><span class="p">:</span> <span class="nx">cover</span><span class="p">;</span> <span class="nx">border</span><span class="o">-</span><span class="nx">radius</span><span class="p">:</span> <span class="mi">8</span><span class="nx">px</span><span class="p">;</span> <span class="nl">cursor</span><span class="p">:</span> <span class="nx">pointer</span><span class="p">;</span> <span class="nl">transition</span><span class="p">:</span> <span class="mi">600</span><span class="nx">ms</span> <span class="nx">cubic</span><span class="o">-</span><span class="nf">bezier</span><span class="p">(</span><span class="mf">0.16</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mf">0.3</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span> <span class="p">}</span> <span class="nl">img</span><span class="p">:</span><span class="nx">hover</span> <span class="p">{</span> <span class="nl">transform</span><span class="p">:</span> <span class="nf">scale</span><span class="p">(</span><span class="mf">1.1</span><span class="p">);</span> <span class="nx">box</span><span class="o">-</span><span class="nx">shadow</span><span class="p">:</span> <span class="mi">0</span> <span class="mi">0</span> <span class="mi">10</span><span class="nx">px</span> <span class="mi">0</span> <span class="nf">rgba</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mf">0.2</span><span class="p">);</span> <span class="p">}</span> <span class="nx">main</span> <span class="p">{</span> <span class="nl">display</span><span class="p">:</span> <span class="nx">flex</span><span class="p">;</span> <span class="nl">gap</span><span class="p">:</span> <span class="mi">36</span><span class="nx">px</span><span class="p">;</span> <span class="nx">align</span><span class="o">-</span><span class="nx">items</span><span class="p">:</span> <span class="nx">center</span><span class="p">;</span> <span class="nx">justify</span><span class="o">-</span><span class="nx">content</span><span class="p">:</span> <span class="nx">center</span><span class="p">;</span> <span class="nl">height</span><span class="p">:</span> <span class="mi">100</span><span class="nx">vh</span><span class="p">;</span> <span class="nl">width</span><span class="p">:</span> <span class="mi">100</span><span class="nx">vw</span><span class="p">;</span> <span class="nl">background</span><span class="p">:</span> <span class="nx">linear</span><span class="o">-</span><span class="nf">gradient</span><span class="p">(</span><span class="nx">black</span><span class="p">,</span> <span class="err">#</span><span class="mi">111</span><span class="p">);</span> <span class="p">}</span> <span class="o">&lt;</span><span class="sr">/style</span><span class="err">&gt; </span></code></pre> </div> <p>We can now see the images rendered on the front-end:</p> <p><a href="proxy.php?url=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"><img src="proxy.php?url=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="Image description" width="800" height="470"></a></p> <h1> Conclusion: </h1> <p>Notion is a powerful tool and works surprisingly well as a headless CMS.</p> <p>My <a href="proxy.php?url=https://trentbrew.com" rel="noopener noreferrer">personal website</a> is powered by Notion, and while there are some trade-offs, Notion has become my goto for projects requiring a light weight CMS.</p> <p>I would love to try some alternatives so please let me know your favorite CMS for small-scale projects!</p> <p><a href="proxy.php?url=https://github.com/trentbrew/notion-cms-tutorial" rel="noopener noreferrer">Full Source Code</a></p> <h1> References </h1> <ul> <li><a href="proxy.php?url=https://developers.notion.com/reference/intro" rel="noopener noreferrer">Notion API Docs</a></li> <li><a href="proxy.php?url=https://nuxt.com/docs/guide/directory-structure/server" rel="noopener noreferrer">Nuxt Server Docs</a></li> </ul> nuxt vue cms tutorial