<?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: Artemy</title>
    <description>The latest articles on DEV Community by Artemy (@tanella).</description>
    <link>https://dev.to/tanella</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%2F2928127%2Fe5dea178-b256-4b3e-a24c-528c305407d1.jpeg</url>
      <title>DEV Community: Artemy</title>
      <link>https://dev.to/tanella</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tanella"/>
    <language>en</language>
    <item>
      <title>I got tired of waiting for the backend, so I built a free fake REST API</title>
      <dc:creator>Artemy</dc:creator>
      <pubDate>Wed, 16 Sep 2026 10:52:41 +0000</pubDate>
      <link>https://dev.to/tanella/i-got-tired-of-waiting-for-the-backend-so-i-built-a-free-fake-rest-api-12f8</link>
      <guid>https://dev.to/tanella/i-got-tired-of-waiting-for-the-backend-so-i-built-a-free-fake-rest-api-12f8</guid>
      <description>&lt;p&gt;Three years on the frontend, and nearly every project started the same way. Designs are ready, the data model has been discussed, the backend doesn't exist. It'll be ready "in a couple of weeks". Sometimes that was even true.&lt;/p&gt;

&lt;p&gt;What do you do for those couple of weeks? You create &lt;code&gt;src/mocks/&lt;/code&gt;, drop some JSON in there, add a &lt;code&gt;setTimeout&lt;/code&gt; of 300 ms so you can at least look at your loaders. Then the backend arrives and it's &lt;code&gt;user_name&lt;/code&gt; instead of &lt;code&gt;userName&lt;/code&gt;, the list comes wrapped in &lt;code&gt;{ items, meta }&lt;/code&gt; instead of a plain array, pagination works differently altogether. And you sit down to rewrite the API layer you thought you had isolated so neatly.&lt;/p&gt;

&lt;p&gt;I went through this enough times that I ended up building a service for myself. It's called &lt;a href="https://m0ckly.site" rel="noopener noreferrer"&gt;mockly&lt;/a&gt;, it's free. Here's how it works and why.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not use something that exists
&lt;/h2&gt;

&lt;p&gt;I did. For a few years I sat on one mock-hosting service. Convenient, simple, exactly what I needed. But it went down. Regularly, and at the worst moment: I sit down to build a page, hit the endpoint, 502. Message the author, silence. Updates twice a year, and a handful of small things just didn't fit how I work.&lt;/p&gt;

&lt;p&gt;Every time it was down I thought: how hard can it be, store some JSON and serve it over REST. Turned out a bit harder than I thought. But I finished it.&lt;/p&gt;

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

&lt;p&gt;mockly hosts fake REST APIs. Sign up (GitHub login works), create a project, add a collection called &lt;code&gt;products&lt;/code&gt;, paste an array of JSON. Done, you have an endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.m0ckly.site/m/&amp;lt;publicId&amp;gt;/products
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It behaves like a real API. Returns a list and a single record, accepts POST, PATCH, PUT, DELETE. The server assigns &lt;code&gt;id&lt;/code&gt;. No keys, no headers, CORS is open, call it straight from the browser. There's a public &lt;code&gt;demo&lt;/code&gt; project, so this snippet works as is:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.m0ckly.site/m/demo/products?limit=2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;meta&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;// items: [{ id: 1, title: 'Compact Mechanical Keyboard', price: 89, ... }, { id: 2, ... }]&lt;/span&gt;
&lt;span class="c1"&gt;// meta:  { total_items: 40, total_pages: 20, current_page: 1, per_page: 2 }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h2&gt;
  
  
  The whole point: one line in .env
&lt;/h2&gt;

&lt;p&gt;This is the idea I built the thing around.&lt;/p&gt;

&lt;p&gt;Data model agreed with the backend team? I put JSON into mockly in the exact shape production will return. Then I write the frontend the normal way: &lt;code&gt;services/api.ts&lt;/code&gt;, react-query hooks, forms, types. All of it real, with real HTTP requests, loaders, errors, empty states.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/services/api.ts&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VITE_API_URL&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getProducts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProductsParams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;BASE&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/products?&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&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="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/queries/products.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useProducts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProductsParams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="na"&gt;queryFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getProducts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Backend is ready. I change one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- VITE_API_URL=https://api.m0ckly.site/m/9f2c1e0a
&lt;/span&gt;&lt;span class="gi"&gt;+ VITE_API_URL=https://api.your-company.com
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The api, queries and mutations layers stay untouched. This is how I've worked for the last few months and it has saved me a lot of nerves.&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%2Frurqshvmt4q47wbtqyk3.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%2Frurqshvmt4q47wbtqyk3.png" alt="You change one line in .env" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it can do
&lt;/h2&gt;

&lt;p&gt;I didn't want "JSON behind a URL". I wanted the behaviour I'm used to from decent backends. Every example below runs against the &lt;code&gt;demo&lt;/code&gt; project.&lt;/p&gt;

&lt;p&gt;Filters: &lt;code&gt;?title=mouse&lt;/code&gt; is a case-insensitive substring match, &lt;code&gt;?title=*mouse*&lt;/code&gt; is a wildcard (anchored at both ends, so &lt;code&gt;Vertical*&lt;/code&gt; matches titles starting with "Vertical"), &lt;code&gt;?price_gte=20&amp;amp;price_lte=90&lt;/code&gt; is a numeric range. There's also &lt;code&gt;?q=mouse&lt;/code&gt;, which searches across every field of a record at once.&lt;/p&gt;

&lt;p&gt;Sorting and pagination: &lt;code&gt;?sortBy=price&amp;amp;order=desc&lt;/code&gt;, numbers compared as numbers, not strings. Pass &lt;code&gt;page&lt;/code&gt; or &lt;code&gt;limit&lt;/code&gt; and the response gets wrapped:&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;"items"&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="err"&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;"meta"&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;"total_items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"total_pages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"current_page"&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;"per_page"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&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;If you always want the envelope, turn it on in the collection settings, along with a default page size.&lt;/p&gt;

&lt;p&gt;Relations. A record has a &lt;code&gt;userId&lt;/code&gt;? Then &lt;code&gt;?_relations=user&lt;/code&gt; embeds the object from the &lt;code&gt;users&lt;/code&gt; collection right into the response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.m0ckly.site/m/demo/posts/1?_relations=user"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;"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;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Why we moved search back to Postgres"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"userId"&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;"user"&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;"Ada Whitfield"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"adaw"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lisbon"&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;Works in reverse too: &lt;code&gt;GET /users/1?_relations=posts&lt;/code&gt; returns the user with all their posts. Several relations, comma-separated: &lt;code&gt;?_relations=posts,todos&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Forced responses. Want to see how the frontend survives a 500 or a 429? Set a fixed response on the collection with any status from 100 to 599 and any body. Remove the status and the collection is back to normal. For debugging error boundaries and react-query retries this is hard to live without.&lt;/p&gt;

&lt;p&gt;Methods are toggled per collection: read, create, update, delete. A disabled one answers 405. Disable the whole project and the endpoints answer 403, data stays put.&lt;/p&gt;

&lt;p&gt;Private resources. Everything is open by link by default. If you need to mock a login flow, a collection can be locked: without a valid token you get 401. It's mock-grade protection, not real security, but enough to debug auth on the frontend.&lt;/p&gt;

&lt;p&gt;Ready-made data. Don't feel like inventing JSON, there are datasets in the spirit of jsonplaceholder so you can start in a minute.&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%2F71laalgvz5ds2sp8gyma.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%2F71laalgvz5ds2sp8gyma.png" alt="Everything a real API gives you" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick start
&lt;/h2&gt;

&lt;p&gt;Try it right now, no sign-up. The &lt;code&gt;demo&lt;/code&gt; project has &lt;code&gt;users&lt;/code&gt;, &lt;code&gt;posts&lt;/code&gt;, &lt;code&gt;comments&lt;/code&gt;, &lt;code&gt;todos&lt;/code&gt; and &lt;code&gt;products&lt;/code&gt;, writes are allowed, data resets every hour:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.m0ckly.site/m/demo/users/1?_relations=posts"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For your own data: go to &lt;a href="https://m0ckly.site" rel="noopener noreferrer"&gt;m0ckly.site&lt;/a&gt;, sign in with GitHub. A &lt;code&gt;default&lt;/code&gt; project is created for you. Create a collection, paste JSON, array or object, any shape. Copy the &lt;code&gt;publicId&lt;/code&gt; from the project page and hit it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.m0ckly.site/m/&amp;lt;publicId&amp;gt;/products?sortBy=price&amp;amp;order=asc&amp;amp;limit=3"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Honest about the limits
&lt;/h2&gt;

&lt;p&gt;This is a tool for prototypes and development, not production. Up to 25 collections per project, 500 records per collection, 500 objects per import. Treat data in open collections as public: no real personal data, no production dumps. If a link leaks, disable the project and the endpoints immediately answer 403.&lt;/p&gt;

&lt;p&gt;Under the hood: Fastify, TypeScript and PostgreSQL with JSONB on my own VPS, Next.js on the frontend. If there's interest I'll write about the architecture separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is d2d
&lt;/h2&gt;

&lt;p&gt;There's b2b, there's b2c. mockly is d2d, dev to dev. I'm a frontend developer, so I built it for my own workflow first, but it's for anyone who needs a working API before the real one exists. I use it myself every day. There's no sales team, no pricing tiers, no quarterly roadmap. There's me, my VPS and your bug reports.&lt;/p&gt;

&lt;p&gt;That's why it's free and will stay free. And that's why what I need most right now is feedback: what's missing, what's annoying, which scenario isn't covered. Drop a comment or ping me on &lt;a href="https://t.me/taneellaa" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt;. It's me answering, not a bot.&lt;/p&gt;

&lt;p&gt;Site: &lt;a href="https://m0ckly.site" rel="noopener noreferrer"&gt;m0ckly.site&lt;/a&gt;&lt;br&gt;
Docs: &lt;a href="https://m0ckly.site/docs" rel="noopener noreferrer"&gt;m0ckly.site/docs&lt;/a&gt;&lt;br&gt;
Service channel: &lt;a href="https://t.me/mocklyapi" rel="noopener noreferrer"&gt;t.me/mocklyapi&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>frontend</category>
      <category>api</category>
      <category>showdev</category>
    </item>
    <item>
      <title>I shipped my first npm package with AI — and it's already in production</title>
      <dc:creator>Artemy</dc:creator>
      <pubDate>Wed, 01 Jul 2026 10:13:27 +0000</pubDate>
      <link>https://dev.to/tanella/i-shipped-my-first-npm-package-with-ai-and-its-already-in-production-2cl9</link>
      <guid>https://dev.to/tanella/i-shipped-my-first-npm-package-with-ai-and-its-already-in-production-2cl9</guid>
      <description>&lt;p&gt;I'm a frontend developer with about three years of experience. Until a few months ago, "publish an npm package" lived on my someday list — the kind of thing you assume requires a deeper relationship with build tooling than you actually have. Then I built one. It's called &lt;a href="https://github.com/artemydottech/daterly" rel="noopener noreferrer"&gt;daterly&lt;/a&gt;, it's a React date picker, and it's already running in internal projects at the company I work for.&lt;/p&gt;

&lt;p&gt;The twist: I wrote most of it with AI — specifically Claude Code and the wider Claude toolset. This is the honest version of how that went. Not "AI built my startup overnight," but a real account of where the AI carried me, where it slowed me down, and what I'd actually keep from the experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why build &lt;em&gt;another&lt;/em&gt; date picker
&lt;/h2&gt;

&lt;p&gt;The internet does not need another React date picker. I know. But we needed &lt;em&gt;this&lt;/em&gt; one.&lt;/p&gt;

&lt;p&gt;At work (sima-land) we lean on &lt;a href="https://react-hook-form.com/" rel="noopener noreferrer"&gt;react-hook-form&lt;/a&gt; everywhere. It's the backbone of how we handle forms across projects. The date picker we were using — react-datepicker — kept getting in the way of that. Two things drove us up the wall:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Styling was a fight.&lt;/strong&gt; Overriding its look to match our design system always felt like wrestling specificity rather than setting a few variables.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The react-hook-form integration was awkward.&lt;/strong&gt; Wiring it into RHF cleanly took more glue code than it should have, every single time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When the same papercut shows up in project after project, it stops being a papercut. We use one tech stack on purpose — a shared, repeatable way of building things so any of us can move between projects without relearning the basics. A date picker that fought both our styling approach and our form library was a crack in that consistency.&lt;/p&gt;

&lt;p&gt;So the real motivation wasn't "wouldn't it be cool to publish a package." It was: &lt;strong&gt;we want one tool that speaks our stack fluently.&lt;/strong&gt; First-class RHF support, styling that bends without a struggle, and the locale/format behavior we actually need (Russian market: &lt;code&gt;ru&lt;/code&gt; locale, &lt;code&gt;dd.MM.yyyy&lt;/code&gt; by default). That's the takeaway I keep coming back to — the package exists to protect a way of working, not to chase npm downloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it ended up being
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/daterly" rel="noopener noreferrer"&gt;daterly&lt;/a&gt; is built on &lt;a href="https://daypicker.dev/" rel="noopener noreferrer"&gt;react-day-picker v9&lt;/a&gt; (headless) and &lt;a href="https://date-fns.org/" rel="noopener noreferrer"&gt;date-fns v4&lt;/a&gt;. The headline features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single date &lt;strong&gt;and&lt;/strong&gt; range picking, controlled or uncontrolled&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;masked text input&lt;/strong&gt; so people can type a date instead of only clicking a calendar&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Arbitrary locales and formats&lt;/strong&gt; via props — the input mask regenerates itself from the &lt;code&gt;dateFormat&lt;/code&gt; you pass&lt;/li&gt;
&lt;li&gt;Optional time selection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-overhead react-hook-form integration&lt;/strong&gt; via a separate &lt;code&gt;daterly/rhf&lt;/code&gt; entry point — if you don't use RHF, you don't pay for it&lt;/li&gt;
&lt;li&gt;Styling through &lt;code&gt;--daterly-*&lt;/code&gt; CSS variables and &lt;code&gt;data-*&lt;/code&gt; state attributes, so you can theme it without touching JavaScript&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The RHF wrappers are generic over your form's value type, so &lt;code&gt;name&lt;/code&gt; gets autocomplete and type-safety:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RHFDatePicker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;daterly/rhf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RHFDatePicker&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;BookingFormValues&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkIn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Check-in date&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Pick a date&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That snippet is the whole reason the project exists, honestly.&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%2Fh6347bqynjjyixqpuoex.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%2Fh6347bqynjjyixqpuoex.png" alt="Two daterly import paths: core DatePicker with no form library, and an opt-in daterly/rhf entry point that only enters your bundle when you use it" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Building it with AI: the good parts
&lt;/h2&gt;

&lt;p&gt;I worked almost entirely inside Claude Code, with Claude Design for the visual side. The parts where the AI genuinely shone were the fiddly, self-contained algorithms — the stuff that's annoying to write by hand but easy to specify.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;input mask&lt;/strong&gt; is the best example. We implemented our own digit-only mask instead of pulling in a masking library, and there's real subtlety in it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rebuild the masked string from raw digits on every keystroke.&lt;/li&gt;
&lt;li&gt;Restore the cursor position with &lt;code&gt;requestAnimationFrame&lt;/code&gt; after React re-renders the input — otherwise the caret jumps to the end and typing feels broken.&lt;/li&gt;
&lt;li&gt;Skip over separators when you press Backspace on one.&lt;/li&gt;
&lt;li&gt;Strip non-digits on paste before masking.&lt;/li&gt;
&lt;li&gt;Validate via a &lt;strong&gt;round-trip check&lt;/strong&gt;: &lt;code&gt;format(parse(masked)) === masked&lt;/code&gt;. That elegantly catches things like &lt;code&gt;32.01.2024&lt;/code&gt; — it parses, but it doesn't format back to the same string, so you know it's invalid.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That round-trip trick is the kind of thing the AI proposed that made me go "oh, that's nice." Describing the edge cases in plain language and getting a working first pass back was a real accelerator. Same with boilerplate-heavy work: dual ESM/CJS build config, TypeScript declaration setup, test scaffolding. Tedious to set up from scratch, quick to generate and then review.&lt;br&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%2Feecdsjt7135i497n46m6.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%2Feecdsjt7135i497n46m6.png" alt="Round-trip validation of the masked date input: 12.05.2026 formats back to itself and is valid, while 32.01.2024 formats to a different string and is rejected" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Building it with AI: the not-so-good parts
&lt;/h2&gt;

&lt;p&gt;It wasn't magic, and pretending otherwise would be dishonest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The limits and the lag.&lt;/strong&gt; I was on the Pro plan, and I hit usage limits more than once mid-flow — exactly when you've built momentum and don't want to stop. On top of that, it noticeably slowed down on a larger context, which broke the rhythm. If you're going to lean on AI for a real project, budget for the friction of &lt;em&gt;waiting&lt;/em&gt; and &lt;em&gt;rationing&lt;/em&gt;, not just the writing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It needs a driver, not a passenger.&lt;/strong&gt; The AI is great at producing a plausible chunk of code. Whether that chunk is &lt;em&gt;correct&lt;/em&gt;, fits the existing patterns, and doesn't quietly reinvent something three files over — that's still on me. The most valuable skill wasn't prompting; it was reading every diff like a code reviewer who doesn't trust the author. A junior who can't evaluate the output would have shipped subtly broken behavior here.&lt;br&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%2F55w4dpqyxk7cl3t02mme.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%2F55w4dpqyxk7cl3t02mme.png" alt="What AI compresses versus what stays on the developer: it handles the mask algorithm, build config, type declarations and boilerplate, while correctness, fit and diff review remain the human's job" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  From "it works on my machine" to an actual package
&lt;/h2&gt;

&lt;p&gt;This is the part I underestimated, and the part I'm most glad I didn't skip. Going from a working component to something installable and maintainable is a whole second project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dual ESM + CJS output&lt;/strong&gt; with proper type declarations, built via &lt;a href="https://github.com/egoist/tsup" rel="noopener noreferrer"&gt;tsup&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Versioning with &lt;a href="https://github.com/changesets/changesets" rel="noopener noreferrer"&gt;Changesets&lt;/a&gt;&lt;/strong&gt;, so every change is intentional and the changelog writes itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI&lt;/strong&gt;, code coverage, and a &lt;strong&gt;bundle-size budget&lt;/strong&gt; (&lt;code&gt;size-limit&lt;/code&gt;) so the thing doesn't quietly balloon.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Component tests&lt;/strong&gt; with Playwright and Vitest — a masked input has enough edge cases that you really want them locked down.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is glamorous, but it's the difference between a gist and a package other people can depend on.&lt;br&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%2Fzhs34htkbjhjscn5vak3.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%2Fzhs34htkbjhjscn5vak3.png" alt="The component took days, the packaging took longer: ESM plus CJS build, Changesets versioning, CI and coverage, size-limit budget, Playwright and Vitest tests, docs" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The 0.4.0 incident
&lt;/h2&gt;

&lt;p&gt;A small, very human lesson. Early on, I ran the Changesets CLI interactively and — distracted — picked &lt;code&gt;minor&lt;/code&gt; instead of &lt;code&gt;patch&lt;/code&gt;. The version jumped to &lt;code&gt;0.4.0&lt;/code&gt; instead of &lt;code&gt;0.3.1&lt;/code&gt;. No undo on a published version; you just live with the gap.&lt;/p&gt;

&lt;p&gt;The fix was a process change I now recommend to anyone: &lt;strong&gt;don't bump versions interactively.&lt;/strong&gt; I have it written into the project's instructions for the AI too — create the changeset file by hand, decide the bump deliberately (&lt;code&gt;patch&lt;/code&gt; for fixes, &lt;code&gt;minor&lt;/code&gt; for new backwards-compatible props, &lt;code&gt;major&lt;/code&gt; for breaking changes), and only then publish. A thirty-second mistake taught me more about release discipline than any tutorial.&lt;br&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%2Fnnfij8xqj5x99u9mdz4m.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%2Fnnfij8xqj5x99u9mdz4m.png" alt="A distracted interactive version bump jumped daterly from 0.3.0 straight to 0.4.0 instead of the expected 0.3.1 patch, with no undo on a published version" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd actually take away
&lt;/h2&gt;

&lt;p&gt;If you're a frontend dev sitting where I was, three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Build for your stack, not for the stars.&lt;/strong&gt; The best reason to make a library is that it removes friction you hit every day. daterly's whole job is to make our standard stack — react-hook-form, our styling approach, our locale defaults — feel native. The npm badge is a side effect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI compresses the boring parts, not the judgment.&lt;/strong&gt; It wrote a lot of correct code fast. It also needed me to catch what it got wrong, ration limited usage, and wait out the slow moments. Treat it as a fast junior pair, not an autopilot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The packaging is the real work.&lt;/strong&gt; The component took days. The build config, versioning, CI, tests, and docs took longer — and that's what makes it usable by anyone other than me.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;daterly is MIT-licensed and on &lt;a href="https://www.npmjs.com/package/daterly" rel="noopener noreferrer"&gt;npm&lt;/a&gt;. Code and docs are on &lt;a href="https://github.com/artemydottech/daterly" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. If you're fighting your date picker's styling or RHF integration, give it a look — and if you build your own thing instead, even better. That's the point.&lt;/p&gt;




&lt;p&gt;I'm also open to freelance work and collaboration. If you've got something you're building and want a hand, or you just want to see what else I've made, it's all at &lt;a href="https://artemy.tech" rel="noopener noreferrer"&gt;artemy.tech&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>react</category>
      <category>javascript</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
