<?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: Alejandro Rodriguez</title>
    <description>The latest articles on DEV Community by Alejandro Rodriguez (@alejandrorodrom).</description>
    <link>https://dev.to/alejandrorodrom</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%2F4069354%2F73659989-e112-47ab-a895-9623bd1b7971.jpg</url>
      <title>DEV Community: Alejandro Rodriguez</title>
      <link>https://dev.to/alejandrorodrom</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alejandrorodrom"/>
    <language>en</language>
    <item>
      <title>Mock Your API in 5 Minutes with JSON (No Backend Required)</title>
      <dc:creator>Alejandro Rodriguez</dc:creator>
      <pubDate>Sun, 09 Aug 2026 16:47:50 +0000</pubDate>
      <link>https://dev.to/alejandrorodrom/mock-your-api-in-5-minutes-with-json-no-backend-required-54c8</link>
      <guid>https://dev.to/alejandrorodrom/mock-your-api-in-5-minutes-with-json-no-backend-required-54c8</guid>
      <description>&lt;p&gt;Your frontend needs the API now. The backend isn’t ready, staging is down, or you want to trigger a 500 without asking anyone to break the server.&lt;/p&gt;

&lt;p&gt;In this article you’ll:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install a local mock server&lt;/li&gt;
&lt;li&gt;Define an endpoint in a JSON file&lt;/li&gt;
&lt;li&gt;Call it with &lt;code&gt;curl&lt;/code&gt; (or from your app) as if it were the real API&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This isn’t an embeddable SDK — it’s a &lt;strong&gt;CLI&lt;/strong&gt; that boots HTTP on your machine. Your app talks to &lt;code&gt;http://localhost:3001&lt;/code&gt; the same way it would talk to staging.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Node.js &amp;gt;= 22.12&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;A project with a &lt;code&gt;package.json&lt;/code&gt; (recommended)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Install
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;http-mock-json &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CLI binary is &lt;code&gt;mock-server&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Initialize
&lt;/h2&gt;



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

&lt;/div&gt;



&lt;p&gt;By default this will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a &lt;code&gt;mocks/&lt;/code&gt; directory&lt;/li&gt;
&lt;li&gt;Add a &lt;code&gt;mock:start&lt;/code&gt; script to your &lt;code&gt;package.json&lt;/code&gt; (&lt;code&gt;mock-server start -p 3001&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Prompt you to create a first mock&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the prompts, you can use something like:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Prompt&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;JSON file name&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;animals&lt;/code&gt; → &lt;code&gt;mocks/animals.json&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Endpoint&lt;/td&gt;
&lt;td&gt;&lt;code&gt;data/animals&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Methods&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The scaffold sets &lt;code&gt;nameResponse&lt;/code&gt; to &lt;code&gt;"success"&lt;/code&gt; and includes two empty responses: &lt;code&gt;success&lt;/code&gt; (&lt;code&gt;200&lt;/code&gt;) and &lt;code&gt;error&lt;/code&gt; (&lt;code&gt;404&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Fill in the response bodies
&lt;/h2&gt;

&lt;p&gt;Open &lt;code&gt;mocks/animals.json&lt;/code&gt; and make it look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data/animals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"nameResponse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"responses"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"statusCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"200"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"animals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lion"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tiger"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"statusCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"404"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"No animals found"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What you need on day one:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Piece&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Top-level key&lt;/td&gt;
&lt;td&gt;Endpoint path (&lt;code&gt;data/animals&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Uppercase method&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nameResponse&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Which scenario is returned by default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;responses[]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Named scenarios with &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;statusCode&lt;/code&gt;, and &lt;code&gt;body&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  4. Start the server
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx mock-server start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, if &lt;code&gt;init&lt;/code&gt; added the script:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Both listen on port 3001 by default (&lt;a href="http://localhost:3001" rel="noopener noreferrer"&gt;http://localhost:3001&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;On start, every mock file is validated. Errors block startup — fix them and run again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch mode is always on:&lt;/strong&gt; save a mock file and the server reloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-i&lt;/span&gt; http://localhost:3001/data/animals
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Use port &lt;code&gt;3001&lt;/code&gt; if you started via the &lt;code&gt;init&lt;/code&gt; script.)&lt;/p&gt;

&lt;p&gt;You should see something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="ne"&gt;OK&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json; charset=utf-8&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"animals"&lt;/span&gt;&lt;span class="p"&gt;:[{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Lion"&lt;/span&gt;&lt;span class="p"&gt;},{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Tiger"&lt;/span&gt;&lt;span class="p"&gt;}]}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your frontend, point the API base URL at that local host. Same HTTP calls — different “backend.”&lt;/p&gt;

&lt;h2&gt;
  
  
  10-second bonus: flip to the error scenario
&lt;/h2&gt;

&lt;p&gt;Change this in the JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"nameResponse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the file (watch reloads) and run &lt;code&gt;curl&lt;/code&gt; again. You now get the 404 with the error message — &lt;strong&gt;without touching the frontend&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That’s the core idea: same URL, different backend behavior, controlled from JSON.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you just got
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A local mock server in minutes&lt;/li&gt;
&lt;li&gt;An endpoint defined in a file the team can read and version&lt;/li&gt;
&lt;li&gt;Success ↔ error by editing a single key&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/http-mock-json" rel="noopener noreferrer"&gt;http-mock-json&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/alejandrorodrom/http-mock-json" rel="noopener noreferrer"&gt;alejandrorodrom/http-mock-json&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://www.rodriguezrom.com/libraries/http-mock-json" rel="noopener noreferrer"&gt;rodriguezrom.com/libraries/http-mock-json&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this helps, drop a ⭐ on the repo or comment with which API you’re mocking.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>frontend</category>
      <category>testing</category>
    </item>
    <item>
      <title>I Built a Free Planning Poker App with SvelteKit and Cloudflare Durable Objects</title>
      <dc:creator>Alejandro Rodriguez</dc:creator>
      <pubDate>Sun, 09 Aug 2026 05:03:49 +0000</pubDate>
      <link>https://dev.to/alejandrorodrom/i-built-a-free-planning-poker-app-with-sveltekit-and-cloudflare-durable-objects-43b4</link>
      <guid>https://dev.to/alejandrorodrom/i-built-a-free-planning-poker-app-with-sveltekit-and-cloudflare-durable-objects-43b4</guid>
      <description>&lt;p&gt;Scrum teams need Planning Poker often — and they rarely want another account, another SaaS login, or a shared spreadsheet.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;&lt;a href="https://planningpoker.free/" rel="noopener noreferrer"&gt;Planning Poker Free&lt;/a&gt;&lt;/strong&gt;: open-source, no signup, create a room, share the link, and estimate in real time.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Live app: &lt;a href="https://planningpoker.free/" rel="noopener noreferrer"&gt;planningpoker.free&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Source: &lt;a href="https://github.com/alejandrorodrom/planning-poker-free" rel="noopener noreferrer"&gt;github.com/alejandrorodrom/planning-poker-free&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffnkk09dnces1vxb6qbg8.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%2Ffnkk09dnces1vxb6qbg8.png" alt="Planning Poker Free board" width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Most estimation tools ask for accounts, orgs, or paid seats before the team can vote. For a 15-minute refinement, that friction is too high.&lt;/p&gt;

&lt;p&gt;I wanted something closer to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a room&lt;/li&gt;
&lt;li&gt;Share &lt;code&gt;/room/{id}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Vote&lt;/li&gt;
&lt;li&gt;Reveal and discuss&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No accounts. Optional password. Done.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rooms:&lt;/strong&gt; public or password-protected&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Roles:&lt;/strong&gt; moderator, voter, observer (with handoff if the moderator drops)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decks:&lt;/strong&gt; modified Fibonacci, Fibonacci, powers of two, T-shirt, sequential&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aggregation:&lt;/strong&gt; consensus, mode, median, or mean&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Voting UX:&lt;/strong&gt; hidden/revealed cards, optional auto-reveal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Teams:&lt;/strong&gt; overall + per-team results&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Export:&lt;/strong&gt; Markdown, CSV, or a photo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6a8u0mwesoyfkupedtul.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%2F6a8u0mwesoyfkupedtul.png" alt="Results view" width="585" height="577"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Choice&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;UI&lt;/td&gt;
&lt;td&gt;Svelte 5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App framework&lt;/td&gt;
&lt;td&gt;SvelteKit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Language&lt;/td&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runtime&lt;/td&gt;
&lt;td&gt;Cloudflare Workers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Realtime state&lt;/td&gt;
&lt;td&gt;Durable Objects + WebSockets&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each room is its own &lt;strong&gt;Durable Object&lt;/strong&gt;: one in-memory state machine plus WebSocket fan-out. No Redis, no always-on Node process — the room wakes when people join and stays consistent while they’re connected.&lt;/p&gt;

&lt;p&gt;That combo is the interesting part for me: realtime multiplayer UX without babysitting infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design choices that mattered
&lt;/h2&gt;

&lt;h3&gt;
  
  
  No accounts on purpose
&lt;/h3&gt;

&lt;p&gt;Session rejoin uses &lt;code&gt;localStorage&lt;/code&gt; so people can refresh and come back. Last active tab wins. Simple, good enough for estimation sessions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Moderators can hand off
&lt;/h3&gt;

&lt;p&gt;If the moderator goes offline, someone else can claim moderation. Real meetings are messy; the tool should survive that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Export is not optional
&lt;/h3&gt;

&lt;p&gt;After the call, teams want notes. Markdown/CSV export makes the session useful beyond the live board.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;Open &lt;a href="https://planningpoker.free/" rel="noopener noreferrer"&gt;planningpoker.free&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Create a room&lt;/li&gt;
&lt;li&gt;Share the link with your team&lt;/li&gt;
&lt;li&gt;Vote and reveal&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you find a bug or want a feature, open an issue or PR on &lt;a href="https://github.com/alejandrorodrom/planning-poker-free" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;If you use it with your team, tell me what broke — or what you wished existed.&lt;/p&gt;




&lt;p&gt;Built and maintained by &lt;a href="https://www.rodriguezrom.com" rel="noopener noreferrer"&gt;Alejandro Rodriguez Romero&lt;/a&gt; / RODRIGUEZROM.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>sveltekit</category>
      <category>cloudflare</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
