<?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: Hendrik Boelcke</title>
    <description>The latest articles on DEV Community by Hendrik Boelcke (@thr0n).</description>
    <link>https://dev.to/thr0n</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%2F586262%2F34bb070d-9aa4-4c96-a993-5ee30e10931b.jpeg</url>
      <title>DEV Community: Hendrik Boelcke</title>
      <link>https://dev.to/thr0n</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thr0n"/>
    <language>en</language>
    <item>
      <title>Claude Code in Docker Sandboxes</title>
      <dc:creator>Hendrik Boelcke</dc:creator>
      <pubDate>Fri, 26 Jun 2026 08:25:46 +0000</pubDate>
      <link>https://dev.to/thr0n/claude-code-in-docker-sandboxes-239g</link>
      <guid>https://dev.to/thr0n/claude-code-in-docker-sandboxes-239g</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This article was also published in &lt;a href="https://thr0n.github.io/claude-code-in-docker-sandboxes-de" rel="noopener noreferrer"&gt;German&lt;/a&gt;!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since generative AI took off, I've found it increasingly hard to find reliable information. It used to be easier to judge whether a Stack Overflow thread or a tutorial would actually help me. For most topics, I knew where to look. Today, there are so many new sources out there and honestly, I struggle to assess the quality of most of them. So I want to take a step back, work through the topic of "Claude Code, but safely", and share what I've learned along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some context
&lt;/h2&gt;

&lt;p&gt;Until recently, my exposure to agentic engineering was fairly limited: conversations with colleagues and friends, the occasional podcast episode, and LinkedIn posts that often felt shallow and hype-driven.&lt;/p&gt;

&lt;p&gt;Then I attended a two-day workshop on agentic engineering at work.&lt;br&gt;
We basically just typed &lt;code&gt;claude&lt;/code&gt; into the terminal and let the agent do its thing.&lt;br&gt;
The presenter talked a lot about "guardrails" but largely ran Claude Code in automode without really addressing what could go wrong.&lt;/p&gt;

&lt;p&gt;And here's the thing: There's a real risk hiding in plain sight. &lt;code&gt;claude&lt;/code&gt; runs with your full user permissions on the host system by default. Concretely, that means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Full filesystem access:&lt;/strong&gt; everything your user can read, including &lt;code&gt;~/.ssh&lt;/code&gt;, &lt;code&gt;~/.aws&lt;/code&gt;, &lt;code&gt;.env&lt;/code&gt; files, and so on&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unrestricted network access:&lt;/strong&gt; arbitrary URLs, downloads, outbound connections, credential exfiltration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process execution:&lt;/strong&gt; shell commands, changing system settings, running package installers, creating cron jobs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access to all shell environment variables&lt;/strong&gt;, including any API keys you've set&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Imagine an AI agent accidentally running &lt;code&gt;rm -rf&lt;/code&gt; in the wrong directory, modifying your Git configuration, or leaking an &lt;code&gt;.env&lt;/code&gt; file. None of these actions are intentional, but they're possible if the agent has unrestricted access.&lt;/p&gt;

&lt;p&gt;That's a steep price for automode, whose main benefit is avoiding the fatigue of confirming every single action.&lt;/p&gt;

&lt;p&gt;Docker Sandboxes addresses this directly: the agent runs in a container with a defined network policy, an isolated filesystem, and no access to host secrets.&lt;br&gt;
You don't have to judge every action on the fly because you set the boundaries upfront.&lt;/p&gt;
&lt;h2&gt;
  
  
  Docker Sandboxes
&lt;/h2&gt;

&lt;p&gt;A former colleague of mine (shoutout to Stefan!) posted on LinkedIn about using Docker Sandboxes and being impressed.&lt;br&gt;
That was enough for me to dig in.&lt;/p&gt;

&lt;p&gt;That brought me back to the original problem from the intro: which source do I trust?&lt;br&gt;
I started with the official &lt;a href="https://docs.docker.com/ai/sandboxes/" rel="noopener noreferrer"&gt;Docker Sandboxes documentation&lt;/a&gt;, which is solid.&lt;br&gt;
But for a first overview, I wanted something more digestible - ideally a video.&lt;br&gt;
YouTube has no shortage of tutorials on this topic, so picking one wasn't easy.&lt;br&gt;
I went with a video that had a decent number of views and good ratings.&lt;/p&gt;

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

&lt;p&gt;It turned out to be about 75% useful. The creator walked through the concept, toured the documentation, and gave me a solid getting-started path. The part about using a local model wasn't relevant to my setup, but everything else landed well.&lt;/p&gt;

&lt;h2&gt;
  
  
  My setup
&lt;/h2&gt;

&lt;p&gt;Installation was straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;docker/tap/sbx    &lt;span class="c"&gt;# install the sbx CLI&lt;/span&gt;
sbx login                      &lt;span class="c"&gt;# log in with your Docker account&lt;/span&gt;
sbx secret &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; anthropic    &lt;span class="c"&gt;# store your Anthropic API key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On first login, you're prompted to choose a default network policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Open        - All connections allowed
2. Balanced    - Default deny, common dev domains allowed
3. Locked Down - Everything blocked unless explicitly allowed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I went with &lt;strong&gt;Balanced&lt;/strong&gt;. It allows AI provider APIs, common package managers, and GitHub, while blocking everything else.&lt;/p&gt;

&lt;p&gt;After that, starting your first sandbox is as simple as:&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="nb"&gt;cd&lt;/span&gt; ~/my-project
sbx run claude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The sbx dashboard
&lt;/h2&gt;

&lt;p&gt;I'd recommend running the sbx dashboard in a separate terminal alongside your session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx   &lt;span class="c"&gt;# opens the interactive dashboard&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From there you can see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which sandboxes are currently running (navigate with arrow keys)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;i&lt;/code&gt; shows details: name, agent, status, mounted directories, resource usage&lt;/li&gt;
&lt;li&gt;A live log of all allowed and blocked network requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The network tab in particular is unexpectedly useful.&lt;br&gt;
You can watch in real time which connections Claude is making:&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%2Fga0k71vjrhfw6heni91x.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%2Fga0k71vjrhfw6heni91x.png" alt="sbx Dashboard" width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  A few hours later
&lt;/h2&gt;

&lt;p&gt;Inside the sandbox, Claude requires an initial &lt;code&gt;/login&lt;/code&gt;. After that, Claude Code starts with &lt;code&gt;--dangerously-skip-permissions&lt;/code&gt; by default. That's intentional: the sandbox itself is the security boundary. It doesn't protect against every possible risk, but it dramatically reduces the blast radius.&lt;/p&gt;

&lt;p&gt;Repository analysis based on a prompt (essentially the content of my current user story) works just as expected, and generating code and test classes runs without issues.&lt;/p&gt;

&lt;p&gt;The first obstacle came when the newly generated integration test couldn't be executed and the solution therefore couldn't be automatically verified.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The ~/.m2 directory doesn't exist — Maven hasn't been run yet in this environment,
so no local repository has been created.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;~/.m2&lt;/code&gt; directory simply doesn't exist in the sandbox. So there are no cached dependencies available. On top of that, the network policy was blocking access to &lt;code&gt;repo.maven.apache.org&lt;/code&gt;, so the Maven Wrapper couldn't bootstrap Maven either.&lt;/p&gt;

&lt;p&gt;At this point I had two options: run the test myself (Claude gave me the exact &lt;code&gt;mvnw&lt;/code&gt; command) or grant Claude the necessary access.&lt;br&gt;
Since I'm comfortable with both &lt;code&gt;repo.maven.apache.org&lt;/code&gt; and the local &lt;code&gt;~/.m2/repository&lt;/code&gt;, I went with the latter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A note on &lt;code&gt;~/.m2/settings.xml&lt;/code&gt;:&lt;/strong&gt; Claude suggested mounting the &lt;code&gt;settings.xml&lt;/code&gt; alongside the repository directory. Since that file can contain credentials for private repositories, I mounted only &lt;code&gt;~/.m2/repository&lt;/code&gt; so the &lt;code&gt;settings.xml&lt;/code&gt; stays off-limits.&lt;/p&gt;
&lt;h2&gt;
  
  
  New sandbox with the m2 mount
&lt;/h2&gt;

&lt;p&gt;You can't add mounts to a running container after the fact, so I had to delete the existing sandbox and create a new one.&lt;/p&gt;

&lt;p&gt;Before doing that, I asked Claude to summarize the session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Summarize what we've done so far and what's next
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude summarized the problem we'd analyzed, the findings, the code changes made, and - most importantly - the list of blockers with their causes and possible solutions. You can persist this context with &lt;code&gt;/memory&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; the &lt;code&gt;/memory&lt;/code&gt; context is stored inside the container by default, which means it's gone when the container is deleted. Before proceeding, make sure the context has actually been saved to your project directory or to &lt;code&gt;~/.claude/MEMORY.md&lt;/code&gt; on the host. I got caught out by this and lost the context. Fortunately, not much had happened yet, so the damage was limited.&lt;/p&gt;

&lt;p&gt;Deleting the old sandbox and creating a new one (including &lt;code&gt;~/.claude&lt;/code&gt; as additional volume mount):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx &lt;span class="nb"&gt;rm&lt;/span&gt; &amp;lt;sandbox-name&amp;gt;

sbx create claude &lt;span class="nt"&gt;--name&lt;/span&gt; shop-dev &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\ &lt;/span&gt;             &lt;span class="c"&gt;# project directory&lt;/span&gt;
  ~/.m2/repository:ro &lt;span class="se"&gt;\ &lt;/span&gt;  &lt;span class="c"&gt;# Maven cache, read-only&lt;/span&gt;
  ~/.claude/              &lt;span class="c"&gt;# Claude directory with write access&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then start it:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;I reconstructed the lost context by asking Claude to look at the existing changes in the project and pick up from there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Allowing network access after the fact
&lt;/h2&gt;

&lt;p&gt;Claude tried to run the integration test again but hit the same wall: the Maven Wrapper needed to download Maven, and the network was still blocked (clearly visible in the sbx dashboard).&lt;/p&gt;

&lt;p&gt;This can be fixed from a separate terminal without restarting the sandbox:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx policy allow network &lt;span class="nt"&gt;--sandbox&lt;/span&gt; shop-dev repo.maven.apache.org
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Back in Claude:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Try it again
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Maven installs. Then Claude notices that our internal artifact registry is also unreachable. The failed request shows up in the dashboard. Since Claude has no credentials for the registry (and shouldn't), it solves the problem on its own: it creates a custom &lt;code&gt;settings.xml&lt;/code&gt; that points to the mounted &lt;code&gt;~/.m2/repository&lt;/code&gt; as a local &lt;code&gt;file://&lt;/code&gt; repository.&lt;/p&gt;

&lt;p&gt;After that, all tests pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;The result on this branch is ready to review (or hand off to a review agent), push to GitHub, and open a pull request.&lt;/p&gt;

&lt;p&gt;This setup does require some upfront configuration: directory mounts, network policies, one-time initialization. It can be simplified with a bash alias or wrapper script, but it's not entirely frictionless.&lt;/p&gt;

&lt;p&gt;What cost me the most time, though, was a hallucination: Claude suggested a &lt;code&gt;settings.json&lt;/code&gt; entry for automatic mounts that simply doesn't exist. That cost me a context I could have kept. A good reminder to always verify AI suggestions about your own toolchain configuration.&lt;/p&gt;

&lt;p&gt;Overall, though, this experiment shows clearly what Docker Sandboxes delivers: by default, only your chosen working directory and a well-defined set of URLs are accessible. Everything else has to be explicitly unlocked.&lt;/p&gt;

&lt;p&gt;Working inside the sandbox, I feel noticeably more relaxed running Claude Code. And at the same time, the restrictions make it very clear just how many requests and file operations Claude makes in automode without one. As a developer, it gives you a meaningful degree of control back over your own system and over resources within your organization. The configuration overhead is absolutely worth it. Running the dashboard alongside Claude Code has made the whole experience feel noticeably more controlled.&lt;/p&gt;




&lt;p&gt;How do you assess this setup? Is there a better approach for mounting additional directories? Any general tips on agentic engineering and working with Claude Code? I look forward to your feedback!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclaimer: I wrote this text, but it was translated with the help of AI.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>security</category>
      <category>docker</category>
      <category>claude</category>
      <category>ai</category>
    </item>
    <item>
      <title>How to: Replace Rollup.js with Vite ⚡️</title>
      <dc:creator>Hendrik Boelcke</dc:creator>
      <pubDate>Sat, 02 Mar 2024 12:49:23 +0000</pubDate>
      <link>https://dev.to/thr0n/how-to-replace-rollupjs-with-vite-4mlj</link>
      <guid>https://dev.to/thr0n/how-to-replace-rollupjs-with-vite-4mlj</guid>
      <description>&lt;p&gt;For me, it was once again time to take care of a project that I haven't worked on for almost a year. As we can see in the output below (the package.json was analyzed using &lt;a href="https://www.npmjs.com/package/npm-check-updates" rel="noopener noreferrer"&gt;npm-check-updates&lt;/a&gt;), the project still uses rollup.js and many libraries have become outdated in the meantime:&lt;/p&gt;

&lt;h2&gt;
  
  
  Current dependencies:
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;@rollup/plugin-commonjs      ^21.1.0  →   ^25.0.7
@rollup/plugin-node-resolve  ^13.3.0  →   ^15.2.3
@rollup/plugin-replace        ^3.1.0  →    ^5.0.5
@rollup/plugin-typescript     ^8.5.0  →   ^11.1.6
@tsconfig/svelte              ^2.0.1  →    ^5.0.2
contentful                    ^9.3.5  →  ^10.6.21
prettier                      ^2.8.8  →    ^3.2.5
prettier-plugin-svelte       ^2.10.1  →    ^3.2.1
rollup                       ^2.79.1  →   ^4.12.0
rollup-plugin-css-only        ^3.1.0  →    ^4.5.2
rollup-plugin-scss            ^3.0.0  →    ^4.0.0
svelte                       ^3.59.2  →   ^4.2.11
svelte-check                 ^2.10.3  →    ^3.6.4
svelte-preprocess            ^4.10.7  →    ^5.1.3
typescript                    ^4.9.5  →    ^5.3.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So it's time to update!&lt;/p&gt;

&lt;h2&gt;
  
  
  Update the application and dependencies
&lt;/h2&gt;

&lt;p&gt;In fact, the introduction of Vite and updating the dependencies were much easier than anticipated. These are the steps I took:&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup the basics:
&lt;/h3&gt;

&lt;p&gt;Run &lt;code&gt;npm create vite@latest&lt;/code&gt;, enter a &lt;code&gt;&amp;lt;project-name&amp;gt;&lt;/code&gt; and choose &lt;code&gt;svelte&lt;/code&gt;. When the initial setup is done copy all newly generated files from &lt;code&gt;./&amp;lt;project-name&amp;gt;&lt;/code&gt; to the actual project directory. Afterwards delete &lt;code&gt;package-lock.json&lt;/code&gt; once and run &lt;code&gt;npm install&lt;/code&gt;. You can also delete &lt;code&gt;rollup.config.js&lt;/code&gt; now.&lt;/p&gt;

&lt;h3&gt;
  
  
  Further tasks
&lt;/h3&gt;

&lt;p&gt;The basic setup is now already done! All I have to do now is to install the dependencies I'm using for my project (like leaflet, contentful, sass, etc.) and replace the generated &lt;code&gt;App.svelte&lt;/code&gt; file with my actual application files.&lt;/p&gt;

&lt;p&gt;Since I'm using some environment variables I also have to prefix the variable names in &lt;code&gt;.env&lt;/code&gt; with &lt;code&gt;VITE_&lt;/code&gt; and replace all &lt;code&gt;process.env.VARIABLE&lt;/code&gt;s with &lt;code&gt;import.meta.env.VITE_VARIABLE&lt;/code&gt; in the source files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bonus task: Run ncu once again and update!
&lt;/h3&gt;

&lt;p&gt;I then checked the dependencies again with ncu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  vgnmap git:&lt;span class="o"&gt;(&lt;/span&gt;feat/replace-rollup&lt;span class="o"&gt;)&lt;/span&gt; ncu
Checking /Users/hendrik/dev/vgnmap/package.json
&lt;span class="o"&gt;[====================]&lt;/span&gt; 16/16 100%

 @playwright/test          1.41.2  →    1.42.1
 @types/node             20.11.19  →  20.11.24
 contentful               10.6.21  →   10.6.22
 prettier-plugin-svelte     3.2.1  →     3.2.2
 svelte                    4.2.11  →    4.2.12
 svelte-check               3.6.4  →     3.6.6
 typescript                 5.2.2  →     5.3.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time there were only minor updates. But while I'm at it, I'll also install these updates! I simply run &lt;code&gt;ncu -u&lt;/code&gt; followed by &lt;code&gt;npm install&lt;/code&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison: build times
&lt;/h2&gt;

&lt;p&gt;Let's take a look at the times required for the production build&lt;/p&gt;

&lt;p&gt;Using Rollup.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  vgnmap git:&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; ✗ npm run build

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; svelte-app@1.0.0 build
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; rollup &lt;span class="nt"&gt;-c&lt;/span&gt;


src/main.ts → public/build/bundle.js...

created public/build/bundle.js &lt;span class="k"&gt;in &lt;/span&gt;2.8s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using Vite:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  vgnmap git:&lt;span class="o"&gt;(&lt;/span&gt;feat/replace-rollup&lt;span class="o"&gt;)&lt;/span&gt; npm run build
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vgnmap@1.1.0 build
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; vite build

 ✓ 46 modules transformed.
dist/index.html                   1.23 kB │ &lt;span class="nb"&gt;gzip&lt;/span&gt;:  0.66 kB
dist/assets/index-DfWY1ihM.css   19.01 kB │ &lt;span class="nb"&gt;gzip&lt;/span&gt;:  7.45 kB
dist/assets/index-CPtvyui6.js   265.34 kB │ &lt;span class="nb"&gt;gzip&lt;/span&gt;: 84.48 kB
✓ built &lt;span class="k"&gt;in &lt;/span&gt;857ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see, the build for the same application is two seconds faster with Vite than with Rollup. Furthermore: Vite also works noticeably faster in development mode! 💛&lt;/p&gt;

&lt;h3&gt;
  
  
  Netlify deployment issues 🚨
&lt;/h3&gt;

&lt;p&gt;Two notes if you also deploy your application to Netlify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don't forget to update the names of your environment variables on netlify.app!&lt;/li&gt;
&lt;li&gt;Vite places the build output in the &lt;code&gt;dist&lt;/code&gt; folder. So you have to change your Netlify Deploment settings, otherwise you'll get a 404 error!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References:
&lt;/h2&gt;

&lt;p&gt;For reference you can take a look at these commits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/thr0n/vgnmap/pull/3/commits/b489e8ce0af15103a7777fd7ada1fbed1cdb4683" rel="noopener noreferrer"&gt;b489e8c - Introduce vite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/thr0n/vgnmap/pull/3/commits/ab4e7b617aca7c498f4ef4e39e1952a66a379c2b" rel="noopener noreferrer"&gt;ab4e7b6 - Drop rollup, run prettier&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For general information about Vite see: &lt;a href="https://vitejs.dev/guide/" rel="noopener noreferrer"&gt;https://vitejs.dev/guide/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>frontend</category>
      <category>svelte</category>
      <category>vite</category>
    </item>
    <item>
      <title>Find a vegan restaurant near you - With the help of Svelte!</title>
      <dc:creator>Hendrik Boelcke</dc:creator>
      <pubDate>Mon, 10 Jan 2022 10:53:16 +0000</pubDate>
      <link>https://dev.to/thr0n/find-a-vegan-restaurant-near-you-with-the-help-of-svelte-24ln</link>
      <guid>https://dev.to/thr0n/find-a-vegan-restaurant-near-you-with-the-help-of-svelte-24ln</guid>
      <description>&lt;p&gt;Trying out &lt;a href="https://svelte.dev/" rel="noopener noreferrer"&gt;Svelte&lt;/a&gt; was on my bucket list for months or maybe even for years.Especially after watching this &lt;a href="https://www.youtube.com/watch?v=AdNJ3fydeao" rel="noopener noreferrer"&gt;talk&lt;/a&gt; by Rich Harris.&lt;/p&gt;

&lt;p&gt;When I was thinking about vegan restaurants in my hometown I finally found a suitable use case: I wanted to show my favourite vegan food locations on a map. That way I can remember where I've been and which restaurants I still want to go to. I also wanted to display information such as the address or excerpts from the menu in a list 🍔🍕🍣 🌱. In addition, the list should focus on the selected restaurant on the map when clicked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;To store and provide the data about the restaurants, I use the content management system &lt;em&gt;Contentful&lt;/em&gt;. In my current client project, I already work with another CMS, but I find Contentful very suitable for managing the restaurants. In particular, the Google Maps function integrated into Contentful was very helpful. With it, I can search for a restaurant and save the coordinates determined by Google Maps in the CMS. With a simple &lt;code&gt;GET&lt;/code&gt; request, I can then retrieve the coordinates together with other restaurant data like address, website, etc. and show them in the frontend 🗺.&lt;/p&gt;

&lt;p&gt;I created the Svelte project with &lt;code&gt;degit&lt;/code&gt; according to these &lt;a href="https://svelte.dev/blog/svelte-for-new-developers" rel="noopener noreferrer"&gt;instructions&lt;/a&gt;. In addition, I use TypeScript and &lt;a href="https://linguinecode.com/post/add-sass-svelte-js" rel="noopener noreferrer"&gt;SCSS&lt;/a&gt;, which can be configured subsequently with little effort. The application itself consists mainly of a &lt;code&gt;GET&lt;/code&gt; request to Contenful to read the restaurant data. I use Leaflet to display the map. &lt;br&gt;
Now when I publish new restaurants in Contenful, they are visible in the frontend after a few seconds. Oh, and have you already discovered the very simple dark mode? 🌚&lt;/p&gt;

&lt;p&gt;The application was &lt;a href="https://vgnmap.netlify.app/" rel="noopener noreferrer"&gt;deployed&lt;/a&gt; on Netlify. A push on the main branch in Github starts the build process on Netlify, where the latest version of the project is then delivered:&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%2Fknkcsuvxu5amof35y99s.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%2Fknkcsuvxu5amof35y99s.png" title="vgnmap" alt="Screenshot of vgnmap" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Svelte Highlights
&lt;/h2&gt;

&lt;p&gt;In preparation for the development of the application, I only spent a few hours reading the pretty good Svelte &lt;a href="https://svelte.dev/tutorial/basics" rel="noopener noreferrer"&gt;tutorial&lt;/a&gt;. So I would not be surprised if the code is not optimal in some places. However, I did recognise a few highlights from Svelte, especially compared to React or Vue.js.&lt;/p&gt;
&lt;h3&gt;
  
  
  Reactive declarations
&lt;/h3&gt;

&lt;p&gt;I really like the idea of reactive declarations. The label of the theme button depends on which theme is currently selected. &lt;br&gt;
&lt;code&gt;themeLabel&lt;/code&gt; will be recomputed when &lt;code&gt;theme&lt;/code&gt; changes and Svelte will afterwards update the DOM. To mark a variable as reactive, the &lt;code&gt;$:&lt;/code&gt; symbol is used.&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="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;themeLabel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Lights off&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Lights on&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;h3&gt;
  
  
  Reactive lists
&lt;/h3&gt;

&lt;p&gt;Reactivity works also on lists! If I select a different city in the checkbox, the list of restaurants to be displayed is refreshed.&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="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;restaurantsToShow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;restaurants&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&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;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;city&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;selectedCity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&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="nx"&gt;b&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;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&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="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&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="mi"&gt;1&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="nx"&gt;position&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="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&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="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="mi"&gt;0&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conditional classes
&lt;/h3&gt;

&lt;p&gt;Conditional classes can also be used very easily. The div below only gets the class &lt;code&gt;"restaurant-section-dark"&lt;/code&gt; assigned if &lt;code&gt;theme&lt;/code&gt; is equal to &lt;code&gt;"dark"&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;
  &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;restaurant-section&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;restaurant&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;section&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dark&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dark&lt;/span&gt;&lt;span class="dl"&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Performance
&lt;/h3&gt;

&lt;p&gt;The Lighthouse Score of the application is very good without me having done anything special for it (at least in the desktop view). Svelte says about itself:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Svelte compiles your code to tiny, framework-less vanilla JS — your app starts fast and stays fast&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Check out the &lt;a href="https://svelte.dev/blog/virtual-dom-is-pure-overhead" rel="noopener noreferrer"&gt;article&lt;/a&gt; about the Virtual DOM and why Svelte doesn't use one!&lt;/p&gt;

&lt;p&gt;The Lighthouse Report seems to confirm 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%2Fb8zltyvn7vuypl5zicj8.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%2Fb8zltyvn7vuypl5zicj8.png" alt="Desktop app lighthouse report" width="800" height="837"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is still room for improvement in the mobile view. In particular, the dependencies on Leaflet and the loading of the map tiles are noticeable. I'll deal with that another time, all right? But that leads us directly to the section with the improvements I can make in the future!&lt;/p&gt;

&lt;h2&gt;
  
  
  Future improvements
&lt;/h2&gt;

&lt;p&gt;I have just mentioned it, but I like to say it again.&lt;br&gt;
In the future, I would like to improve the performance on mobile devices. If you have any tips for improvement (especially for Leaflet), I'd definitely appreciate a message from you!&lt;/p&gt;

&lt;p&gt;I would also like to add and display more information about the restaurants. For example, I am thinking of photos, ratings and opening hours. So far, there are also only a few restaurants in Hamburg, Berlin and Munich.&lt;br&gt;
But other cities will surely follow. At the latest when I add restaurants from other countries, I will also have to do some internationalisation.&lt;/p&gt;

&lt;p&gt;And I quickly finished the project in my spare time between Christmas and New Year 🙈 The code base should therefore still be tidied up and the city coordinates need to be removed from the code. But before I start, I should definitely write a few tests...&lt;/p&gt;

&lt;p&gt;If you like you can try out veganmap &lt;a href="https://vgnmap.netlify.app/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Thanks for reading! &lt;/p&gt;

</description>
      <category>showdev</category>
      <category>svelte</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to solve an everyday problem with Node.js, Gatsby and Netlify</title>
      <dc:creator>Hendrik Boelcke</dc:creator>
      <pubDate>Mon, 01 Mar 2021 06:54:41 +0000</pubDate>
      <link>https://dev.to/thr0n/how-to-solve-an-everyday-problem-with-node-js-gatsby-and-netlify-11fm</link>
      <guid>https://dev.to/thr0n/how-to-solve-an-everyday-problem-with-node-js-gatsby-and-netlify-11fm</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;I built a random episode generator for audio dramas on Spotify before Netflix did the same for its series 😉. Here I outline the implementation based on Node.js, Gatsby and Netlify.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Motivation
&lt;/h1&gt;

&lt;p&gt;I really love radio dramas, especially &lt;em&gt;Die drei ???&lt;/em&gt; ("Three Investigators"). And I'm not alone with that. The series has now been in production since 1979 and there are now over 200 episodes, live shows, Christmas specials, merchandise, etc. Over fifty million records have been sold so far. So there are a lot of people out there who also like to listen to them as much as I do (or even more).&lt;/p&gt;


&lt;div class="ltag__wikipedia--container"&gt;
  &lt;div class="ltag__wikipedia--header"&gt;
    &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Sew3uq9H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/wikipedia-logo-0a3e76624c7b1c3ccdeb9493ea4add6ef5bd82d7e88d102d5ddfd7c981efa2e7.svg" class="ltag__wikipedia--logo" alt="Wikipedia Logo" width="128" height="128"&gt;
    &lt;a href="https://en.wikipedia.org/wiki/Three_Investigators#Germany" rel="noopener noreferrer"&gt;Three Investigators - Germany&lt;/a&gt;
  &lt;/div&gt;
  &lt;div class="ltag__wikipedia--extract"&gt;
&lt;p&gt;The Three Investigators books have always been very popular in Germany. They are known there as &lt;i&gt;Die drei&lt;span&gt;&amp;nbsp;&lt;/span&gt;???&lt;/i&gt; (&lt;i&gt;Die drei Fragezeichen&lt;/i&gt;, meaning "The Three Question Marks"). Jupiter Jones was renamed as "Justus Jonas", a German adaption of his original name, while Peter Crenshaw is named "Peter Shaw". Bob Andrews retained his original name. The chauffeur's name is Morton.&lt;/p&gt;

&lt;p&gt;In 1964 the Random House publishing company and the Kosmos publishing company started to publish the translated 43 original books. In 1979 the German publisher Europa started a radio drama based on those original 43 books. In 1993 Kosmos started to publish new written books by German authors which were and still are continued as radio dramas in Germany. All in all, this would result in a canon of over 200 books (6 books per year, 3 during spring and 3 during autumn) and 201 radio dramas published as…&lt;/p&gt;
&lt;/div&gt;
  &lt;div class="ltag__wikipedia--btn--container"&gt;
    
      &lt;a href="https://en.wikipedia.org/wiki/Three_Investigators#Germany" rel="noopener noreferrer"&gt;View on Wikipedia&lt;/a&gt;
    
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;In addition, of course, there are a variety of other series. &lt;em&gt;TKKG&lt;/em&gt;, &lt;em&gt;Fünf Freunde&lt;/em&gt;, &lt;em&gt;Point Whitmark&lt;/em&gt;, just to name a few. But &lt;em&gt;Die drei ???&lt;/em&gt; is still my all-time favorite. I found a good article on &lt;a href="https://www.fluentu.com/blog/german/german-dramas/"&gt;Fluentu.com&lt;/a&gt; that will give you an overview of the topic if you're more interested.&lt;/p&gt;

&lt;p&gt;Thanks to Spotify I have access to an almost endless number of radio dramas. Nevertheless, it is often difficult to find an episode that fits my mood or situation. That's why I often ask my wife for a random episode ID. Unfortunately, my wife is not a good randomizer. When I ask her for an episode ID, she answers "73!" most of the time 😉. But episode #73 of &lt;em&gt;Die drei ???&lt;/em&gt; is "Poltergeist" which is not a good episode if you want to listen to something relaxing while you try to fall asleep 👻 😱&lt;/p&gt;

&lt;p&gt;So, there was only one way out: I had to build my personal random episode generator. I was aware that this one will sometimes make inappropriate suggestions to me, but I was hoping that the poltergeist would be suggested to me less often.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rQQNh-QC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dzryh89sdwfz2kkx5gkq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rQQNh-QC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dzryh89sdwfz2kkx5gkq.png" alt="Decisions" width="880" height="644"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Exploring possible solutions
&lt;/h1&gt;

&lt;p&gt;To be honest, I started this project about two and a half years ago, kept discarding ideas and starting over again. I have gone down many different paths to find a solution that fits my needs. First, I wanted to use Clojure for a small backend application and React to implement the frontend. I also made it to crawl Wikipedia for episode titles and parse that information and fit it into a simple database schema. But after a few days, I lost focus on this rather complicated approach. There was a gap between the episode suggestion and the play button to start playback. Why should I use a suggestion application if I have to type this suggestion into Spotify by hand afterwards? Or even worse: If I would then have to search for the episode for a long time in a CD collection?&lt;/p&gt;

&lt;p&gt;Back in 2020 I stumbled over the idea of &lt;a href="https://jamstack.org/"&gt;Jamstack&lt;/a&gt; applications ("JAM" means JavaScript, APIs and Markup). Jamstack promises better performance, higher security, cheap and easy scaling and a better developer experience. To explain the principle very briefly: You can use a static site generator like Gatsby.js to create static sites from Markdown, JSON, APIs, etc using React. Afterwards, you can deploy your static HTML, CSS and JavaScript to a provider of your choice, e.g. GitHub pages or Netlify! If you want to learn more about Jamstack there are a lot of resources about it out there!&lt;/p&gt;

&lt;p&gt;Thinking about the Jamstack approach and my Spotify account I was approaching a new solution. Fortunately, I found that Spotify provides a comprehensive and well documented &lt;a href="https://developer.spotify.com/"&gt;API&lt;/a&gt;. So, I came up with the idea of developing a web based Jamstack application. It should get the information about the different episodes directly from the Spotify API and provide a button that will play the episode on Spotify without redirection.&lt;/p&gt;

&lt;h1&gt;
  
  
  Episode importer
&lt;/h1&gt;

&lt;p&gt;But first things first. I had to access the Spotify API in order to fetch episode information and create a random playback button. So, I created a Spotify developer account and generated an API key. With this key in hand, I could use ReST to get information about artists and albums. Artists of interest are stored in a small JSON object including their name and Spotify artist id. Then I have created a small Node.js application that fetches all episodes of every artist from the configuration object. Each sequence was then saved in JSON format. There ain't no magic in this. Just some ReST calls, basic error handling and file I/O.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AULs7Zyv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4dcsbhaoio11i23qvsbs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AULs7Zyv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4dcsbhaoio11i23qvsbs.png" alt="random-episode-importer" width="451" height="171"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Later I added some more logic to the application. So now only the latest episodes are retrieved and the episode objects are no longer persisted locally, but in a cloud database.&lt;/p&gt;

&lt;p&gt;You can see the source files of random-episode-importer on &lt;a href="https://github.com/thr0n/random-episode-importer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/thr0n"&gt;
        thr0n
      &lt;/a&gt; / &lt;a href="https://github.com/thr0n/random-episode-importer"&gt;
        random-episode-importer
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h1&gt;
  
  
  Episode frontend
&lt;/h1&gt;

&lt;p&gt;Once I finally had the episodes in JSON format as input (without scraping, woohoo!), it was time to set up Gatsby.js. To get started, I just followed the &lt;a href="https://www.gatsbyjs.com/docs/tutorial/"&gt;Gatsby tutorial&lt;/a&gt;. In the tutorial you'll will learn everything you need to know to create UI components, add styling, read JSON files as input, access the imported JSON data using GraphQL, automatically create pages from data and much more.&lt;/p&gt;

&lt;p&gt;I started with two GraphQL queries: A list of all artists and a list of all episodes grouped by the artist id. Based on this data I created an overview page that shows all artists that have been imported using the episode importer. With a click on an artist, a random episode determined at the time of rendering is displayed. This works with the help of a page generated specifically for the episode, to which the browser gets redirected. To play the episode on Spotify, I just need to click on the displayed cover. Each click on an artist also defines a new random episode for the next run. This mechanism was implemented quite simply using &lt;code&gt;Math.random()&lt;/code&gt; 🙈.&lt;/p&gt;

&lt;p&gt;Recently, I added an additional page that shows the three latest releases for each artist. This is very handy if you don't want to miss any new releases of your favorite artist! But that's not the only feature that I've added. Additionally, there is an Auth0 login form if you want to access random episode. This was unfortunately necessary from my point of view, because I don't own the rights to the names and cover images I display in the application 😔. All I can give you is my promise that it is a lot of fun to work with the Spotify API (at least if you're a music lover) and build static websites with Gatsby and React.&lt;/p&gt;

&lt;p&gt;Here's a small GIF recording of random episode to give you a hint of the look and feel of the application. On the first screen, it shows the artist overview as well as a short info text of random episode's purpose. After that, you can see how clicking on a tile redirects the browser to a random episode for the selected artist:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uJgikWCC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://github.com/thr0n/random-episode-frontend/raw/master/demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uJgikWCC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://github.com/thr0n/random-episode-frontend/raw/master/demo.gif" alt="random-episode showcase" width="366" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The source code of random-episode-frontend is also available on &lt;a href="https://github.com/thr0n/random-episode-frontend"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/thr0n"&gt;
        thr0n
      &lt;/a&gt; / &lt;a href="https://github.com/thr0n/random-episode-frontend"&gt;
        random-episode-frontend
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      You can read about random-episode on my blog 🙃
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h1&gt;
  
  
  Deployment
&lt;/h1&gt;

&lt;p&gt;If you're looking for a way to host a static website, you'll probably come across Netlify fairly quickly. Netlify simplifies the deployment and hosting of a web application (for example developed with Gatsby, Next.js or plain React, Vue or, Angular) immensely. In the simplest case, Netlify basically only needs access to my Git repository and a suitable build script. Then a push in the repository is enough to execute a build and a subsequent deployment on Netlify. So I don't have to worry about DevOps topics or infrastructure components, I just have to write application code. After deployment, I can reach my app from anywhere there is internet.&lt;/p&gt;

&lt;p&gt;I also created a small script that runs the episode importer and triggers a build on Netlify using a POST request. In this way it's possible to read the latest episode data from the cloud database without pushing to GitHub. To do this, I integrated an update mechanism into the Netlify build that reads the latest episodes from the database.&lt;/p&gt;

&lt;p&gt;Since I added Auth0 login to random episode the application is for my eyes only (and of course the eyes of my wife). But I can tell you that it's fun to use the app and that it's a good solution to prevent situations I described in the introduction. So please feel free to fork the repository and setup your own random episode generator!&lt;/p&gt;

&lt;h1&gt;
  
  
  Future improvements
&lt;/h1&gt;

&lt;p&gt;The project isn't quite finished yet. There are a few topics on my to do list I'd like to do in the near future:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add an unprotected welcome page, so visitors can get an idea of what is going on at random episode&lt;/li&gt;
&lt;li&gt;Add TypeScript to the episode importer. In my current customer project we use TypeScript and I don't want to miss it anymore&lt;/li&gt;
&lt;li&gt;At the moment I'm using Firebase as cloud database. I've been reading bad things about it a lot lately, so maybe I'll give Back4App a try&lt;/li&gt;
&lt;li&gt;Finally, I would like to automate the build and deployment on Netlify so that I can always see all the new episodes on Friday without running my script locally&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Are you also working with Gatsby or Netlify? Or have you already used the Spotify API yourself? Or do you have a favorite radio drama series as well? 😉 I would be happy to read about all of these things in the comments!&lt;/p&gt;

&lt;p&gt;I hope that you now can't wait to play around with Gatsby, Netlify, or the Spotify API! Thanks for reading!&lt;/p&gt;

</description>
      <category>gatsby</category>
      <category>showdev</category>
      <category>react</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
