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.
What do you do for those couple of weeks? You create src/mocks/, drop some JSON in there, add a setTimeout of 300 ms so you can at least look at your loaders. Then the backend arrives and it's user_name instead of userName, the list comes wrapped in { items, meta } 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.
I went through this enough times that I ended up building a service for myself. It's called mockly, it's free. Here's how it works and why.
Why not use something that exists
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.
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.
What it is
mockly hosts fake REST APIs. Sign up (GitHub login works), create a project, add a collection called products, paste an array of JSON. Done, you have an endpoint:
https://api.m0ckly.site/m/<publicId>/products
It behaves like a real API. Returns a list and a single record, accepts POST, PATCH, PUT, DELETE. The server assigns id. No keys, no headers, CORS is open, call it straight from the browser. There's a public demo project, so this snippet works as is:
const res = await fetch('https://api.m0ckly.site/m/demo/products?limit=2')
const { items, meta } = await res.json()
// items: [{ id: 1, title: 'Compact Mechanical Keyboard', price: 89, ... }, { id: 2, ... }]
// meta: { total_items: 40, total_pages: 20, current_page: 1, per_page: 2 }
The whole point: one line in .env
This is the idea I built the thing around.
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: services/api.ts, react-query hooks, forms, types. All of it real, with real HTTP requests, loaders, errors, empty states.
// src/services/api.ts
const BASE = import.meta.env.VITE_API_URL
export const getProducts = (params: ProductsParams) =>
fetch(`${BASE}/products?${new URLSearchParams(params)}`).then(r => r.json())
// src/queries/products.ts
export const useProducts = (params: ProductsParams) =>
useQuery({ queryKey: ['products', params], queryFn: () => getProducts(params) })
Backend is ready. I change one line:
- VITE_API_URL=https://api.m0ckly.site/m/9f2c1e0a
+ VITE_API_URL=https://api.your-company.com
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.
What it can do
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 demo project.
Filters: ?title=mouse is a case-insensitive substring match, ?title=*mouse* is a wildcard (anchored at both ends, so Vertical* matches titles starting with "Vertical"), ?price_gte=20&price_lte=90 is a numeric range. There's also ?q=mouse, which searches across every field of a record at once.
Sorting and pagination: ?sortBy=price&order=desc, numbers compared as numbers, not strings. Pass page or limit and the response gets wrapped:
{
"items": [ ... ],
"meta": { "total_items": 40, "total_pages": 4, "current_page": 2, "per_page": 10 }
}
If you always want the envelope, turn it on in the collection settings, along with a default page size.
Relations. A record has a userId? Then ?_relations=user embeds the object from the users collection right into the response:
curl "https://api.m0ckly.site/m/demo/posts/1?_relations=user"
{
"id": 1,
"title": "Why we moved search back to Postgres",
"userId": 1,
"user": { "id": 1, "name": "Ada Whitfield", "username": "adaw", "city": "Lisbon" }
}
Works in reverse too: GET /users/1?_relations=posts returns the user with all their posts. Several relations, comma-separated: ?_relations=posts,todos.
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.
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.
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.
Ready-made data. Don't feel like inventing JSON, there are datasets in the spirit of jsonplaceholder so you can start in a minute.
Quick start
Try it right now, no sign-up. The demo project has users, posts, comments, todos and products, writes are allowed, data resets every hour:
curl "https://api.m0ckly.site/m/demo/users/1?_relations=posts"
For your own data: go to m0ckly.site, sign in with GitHub. A default project is created for you. Create a collection, paste JSON, array or object, any shape. Copy the publicId from the project page and hit it:
curl "https://api.m0ckly.site/m/<publicId>/products?sortBy=price&order=asc&limit=3"
Honest about the limits
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.
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.
This is d2d
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.
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 Telegram. It's me answering, not a bot.
Site: m0ckly.site
Docs: m0ckly.site/docs
Service channel: t.me/mocklyapi



Top comments (0)