Mock APIs with natural language
Describe the API your project needs. Grim creates real, stateful endpoints locally — seeded with data that fits your schema. Just focus on building your frontend!
❯ i'm building a community feed — users, posts
with upvote counts and topic tags, and
comment threads. seed it heavy, i need
enough records to stress test the feed.
⠸ Asking Claude...
3 resources · 650 records
· users 50 records
· posts 200 records
· comments 400 records
↳ posts.userId → users.id
↳ comments.postId → posts.id
❯ █
Needs an Anthropic API key · GitHub →
It's a real API.
Curl it.
Whether you're building an MVP, a personal project or a new feature, Grim has you covered with disposable REST endpoints that you can tweak however you like.
Every resource you describe becomes a live endpoint. Full CRUD — filter, sort, create, update, delete. No config files, no schema definitions.
$ curl 'localhost:7777/posts?sort=likes:desc&limit=3'
[
{ "id": 42, "title": "How I built my side project", "likes": 2841, "published": true },
{ "id": 7, "title": "10 lessons from shipping fast", "likes": 1204, "published": true },
{ "id": 23, "title": "Why I chose Bun over Node", "likes": 891, "published": true }
]
$ curl -X POST localhost:7777/posts \
-H 'Content-Type: application/json' \
-d '{"userId": 3, "title": "My first post", "likes": 0}'
{ "id": 201, "userId": 3, "title": "My first post", "likes": 0, "published": false }
GraphQL, auto-generated.
Every resource gets a GraphQL endpoint automatically — queries, mutations, nested resolvers. Same data, no schema to write.
$ curl -X POST localhost:7777/__cast/graphql \
-H 'Content-Type: application/json' \
-d '{"query": "{ posts(limit: 3) { id title likes author { name } } }"}'
{
"data": {
"posts": [
{ "id": 42, "title": "How I built my side project", "likes": 2841, "author": { "name": "James Park" } },
{ "id": 7, "title": "10 lessons from shipping fast", "likes": 1204, "author": { "name": "Elena Rodriguez" } },
{ "id": 23, "title": "Why I chose Bun over Node", "likes": 891, "author": { "name": "Alice Chen" } }
]
}
}
Data that reacts.
Not just sits there.
Endpoints can react to each other. Describe the logic — create an order and stock decrements, post a comment and the count increments, delete a user and their content is removed. Grim wires it so your mock behaves like the real thing.
❯ i'm building a storefront. when i POST an
order, subtract the quantity from the
product's stock. when it hits zero, flip
the status to "out_of_stock" — my UI reads
that field to disable the buy button.
Also: 2 triggers wired
❯ █
$ curl localhost:7777/products/12
{ "id": 12, "name": "Wireless Headphones", "stock": 7, "status": "in_stock" }
$ curl -X POST localhost:7777/orders \
-H 'Content-Type: application/json' \
-d '{"productId": 12, "quantity": 7}'
{ "id": 88, "productId": 12, "quantity": 7, "status": "confirmed" }
$ curl localhost:7777/products/12
{ "id": 12, "name": "Wireless Headphones", "stock": 0, "status": "out_of_stock" }
stock went from 7 → 0, status flipped automatically
Change the schema
while it's running.
Describe what changed. Grim updates the schema and backfills existing records — no restart, no data loss. Useful when the real API spec changes and you need your mock to keep up.
$ curl -X POST localhost:7777/__cast/evolve \
-H 'Content-Type: application/json' \
-d '{"name": "posts", "changes": "add a views counter, rename likes to reactions, drop published. i'\''m adding an api key layer — require X-API-Key on write requests"}'
{
"updated": 200,
"added": ["views"],
"renamed": { "likes": "reactions" },
"removed": ["published"],
"guards": 1
}
Want pagination?
Describe what you need — cursor, offset, page-based. Grim configures it. Your frontend gets real paginated responses to build against.
❯ i need the posts feed paginated — 20 per page,
newest first. the response should wrap the
array in "items" and give me a "nextToken"
field to fetch the next page.
· posts ← pagination configured
❯ █
$ curl 'localhost:7777/posts?limit=2'
{
"items": [
{ "id": 42, "title": "How I built my side project", "likes": 2841 },
{ "id": 7, "title": "10 lessons from shipping fast", "likes": 1204 }
],
"nextToken": "eyJpZCI6N30",
"has_more": true
}
$ curl 'localhost:7777/posts?nextToken=eyJpZCI6N30&limit=2'
{
"items": [
{ "id": 23, "title": "Why I chose Bun over Node", "likes": 891 },
{ "id": 31, "title": "Building my first side project", "likes": 445 }
],
"nextToken": "eyJpZCI6MzF9",
"has_more": true
}
Need a header guard
on a route?
Describe the rule — bearer token, API key, custom header. Grim enforces it. Your frontend gets real 401s to handle instead of skipping auth entirely during development.
❯ i'm adding auth to my api — i need write
operations protected. require a bearer token
on POST, PATCH and DELETE. return 401 if
it's missing or invalid.
Also: 1 header guard
❯ █
$ curl -X POST localhost:7777/posts \
-d '{"userId": 3, "title": "test"}'
{ "error": "Missing or invalid Authorization header" } ← 401
$ curl -X POST localhost:7777/posts \
-H 'Authorization: Bearer my-token' \
-d '{"userId": 3, "title": "test"}'
{ "id": 202, "userId": 3, "title": "test", "likes": 0 } ← 201
Full OAuth 2.0 provider,
from a description.
Describe the flow. Grim runs a real OAuth 2.0 / OIDC provider locally — auth code, PKCE, client credentials, scopes, refresh tokens, introspection, discovery. Build and test your auth integration against it before the real one exists.
❯ i'm testing oauth in my SPA. users log in
and get a token. three permission levels:
read for guests, write for editors, admin
for everything. use pkce — i don't want to
deal with a client secret in the browser.
OAuth 2.0 configured · authorization_code + pkce
· scopes read write admin
· authorize /__cast/oauth/authorize
· token /__cast/oauth/token
· discovery /__cast/oauth/.well-known/openid-configuration
❯ █
Make it fail
on purpose.
Inject failures, latency, and rate limits into any endpoint. Test your error states and retry logic against the mock before the real backend exists.
❯ fail 20% of POST /comments with a 503
so i can test my retry logic.
add 100–400ms latency to GET /posts
so i can see my loading skeleton.
Also: 2 chaos rules
❯ █
Browse it live.
/web opens a web UI for your running API — browse records, filter, create, delete, inspect active chaos rules and guards.
❯ /web
Opened http://localhost:7777/__cast
❯ █
| id | name | tag | |
|---|---|---|---|
| 1 | Alice Chen | alice@example.com | frontend |
| 2 | Marcus Williams | marcus@example.com | backend |
| 3 | Sophie Turner | sophie@example.com | design |
| 4 | James Park | james@example.com | devops |
| 5 | Elena Rodriguez | elena@example.com | frontend |
| id | userId | title | likes | published |
|---|---|---|---|---|
| 42 | 4 | How I built my side project | 2841 | true |
| 7 | 9 | 10 lessons from shipping fast | 1204 | true |
| 8 | 3 | Async/await finally explained | 1089 | true |
| 23 | 2 | Why I chose Bun over Node | 891 | true |
| 31 | 7 | Building my first side project | 445 | true |
| 18 | 1 | CSS tips I wish I knew earlier | 312 | false |
| id | postId | userId | body |
|---|---|---|---|
| 1 | 42 | 3 | Really helpful, thanks! |
| 2 | 7 | 11 | Been waiting for this post. |
| 3 | 23 | 5 | Saved this one. |
| 4 | 8 | 7 | Agree completely. |
| 5 | 42 | 2 | This changed how I think. |
Fork the state.
Wreck it safely.
Clone the entire running state into an isolated workspace. Route requests with X-Workspace and only that fork mutates — useful for parallel test runs, per-branch environments, or sharing a sandbox with a teammate.
$ curl -X POST localhost:7777/__cast/fork \
-d '{"name": "qa-branch"}'
{ "id": "a7f3b2c1", "name": "qa-branch", "resources": 3, "records": 650 }
# QA hits the fork — only the fork mutates
$ curl -X DELETE localhost:7777/posts/42 \
-H 'X-Workspace: a7f3b2c1'
{ "deleted": true }
# main state is untouched
$ curl localhost:7777/posts/42
{ "id": 42, "title": "How I built my side project", "likes": 2841 } ← still there
Import a spec.
Export your state.
Point Grim at an existing OpenAPI or Swagger spec and it imports the resources directly. Export your current state — resources, data, rules — as JSON to share with a teammate or restore later.
$ curl -X POST localhost:7777/__cast/openapi \
-H 'Content-Type: application/json' \
-d '{"url": "https://petstore3.swagger.io/api/v3/openapi.json"}'
{ "imported": ["pets", "store", "users"], "records": 90 }
$ curl localhost:7777/__cast/export > session.json
# send it to a teammate, commit it, restore it later
$ curl -X POST localhost:7777/__cast/import \
-H 'Content-Type: application/json' \
-d @session.json
{ "imported": 3, "message": "State restored." }
What are you building?
Type what your project actually needs. These are examples.