Jsond is a blazingly fast and 90% spec-compliant Rust reimplementation of the npm package json-server.
Built on Tokio and Axum.
It includes features from the old package and the upcoming v1 release.
Note
I've set up this project to publish its binaries automatically on release via Github Actions, but that doesn't happen currently due to some issues with my Github Account. Until that is resolved, you can use options 2a or 2b to run this project.
At the moment, it seems I may have to migrate the code to another platform, possibly Codeberg
Create a db.json file:
{
"posts": [
{ "id": "1", "title": "Hello", "views": 100 },
{ "id": "2", "title": "World", "views": 200 }
],
"comments": [{ "id": "1", "text": "Nice post", "postId": "1" }],
"profile": {
"name": "admin"
}
}Start the server:
jsond db.jsonThe server starts at http://localhost:3000.
Query your API:
curl http://localhost:3000/posts
curl http://localhost:3000/posts/1
curl http://localhost:3000/posts?views:gt=100
curl http://localhost:3000/posts?_sort=-views&_page=1&_per_page=10- JSOND (Json Daemon)
Download and run the install script:
curl -fsSL https://raw.githubusercontent.com/princemuel/jsond/main/install.sh | sh
jsond db.jsonOr manually:
wget https://raw.githubusercontent.com/princemuel/jsond/main/install.sh
sh install.sh
jsond db.jsonRequires Rust 1.84+.
# Install the latest rust (if you don't have rust)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shgit clone git@github.com:princemuel/jsond.gitcargo install --path .jsond db.jsoncargo build --release
./target/release/jsond db.jsonCreate a db.json or db.json5 file. Each top-level key becomes a REST resource.
{
"posts": [
{ "id": "1", "title": "a title", "views": 100 },
{ "id": "2", "title": "another title", "views": 200 }
],
"comments": [
{ "id": "1", "text": "a comment about post 1", "postId": "1" },
{ "id": "2", "text": "another comment about post 1", "postId": "1" }
],
"profile": {
"name": "typicode"
}
}View the db.json5 example
{
posts: [
{ id: "1", title: "a title", views: 100 },
{ id: "2", title: "another title", views: 200 },
],
comments: [
{ id: "1", text: "a comment about post 1", postId: "1" },
{ id: "2", text: "another comment about post 1", postId: "1" },
],
profile: {
name: "typicode",
},
}You can read more about the JSON5 format here.
{
"posts": [{ "id": "1", "title": "Hello", "author": "alice", "views": 100 }]
}{
"profile": {
"name": "admin",
"email": "admin@example.com"
}
}# List all posts
curl http://localhost:3000/posts
# Get a single post
curl http://localhost:3000/posts/1
# Create a post
curl -X POST http://localhost:3000/posts \
-H "Content-Type: application/json" \
-d '{"title":"New Post","views":0}'
# Update a post (full replace)
curl -X PUT http://localhost:3000/posts/1 \
-H "Content-Type: application/json" \
-d '{"title":"Updated","views":150}'
# Partially update a post
curl -X PATCH http://localhost:3000/posts/1 \
-H "Content-Type: application/json" \
-d '{"views":250}'
# Delete a post
curl -X DELETE http://localhost:3000/posts/1Use field=value for equality or field:operator=value for conditions:
# Exact match
curl http://localhost:3000/posts?title=Hello
# Greater than
curl http://localhost:3000/posts?views:gt=100
# Less than or equal
curl http://localhost:3000/posts?views:lte=200
# Not equal
curl http://localhost:3000/posts?author:ne=alice
# In list
curl http://localhost:3000/posts?id:in=1,2,3
# Contains (case-insensitive substring)
curl http://localhost:3000/posts?title:contains=ello
# Starts with
curl http://localhost:3000/posts?title:startsWith=He
# Ends with
curl http://localhost:3000/posts?title:endsWith=oUse dot notation to filter on nested properties:
curl http://localhost:3000/posts?author.name=alice
curl http://localhost:3000/posts?meta.tags:contains=rustCombine parameters with &:
# Posts with views > 50 AND title contains "rust"
curl 'http://localhost:3000/posts?views:gt=50&title:contains=rust'For advanced queries, use _where with a JSON object:
# Posts with (views > 100) OR (author name < "m")
curl 'http://localhost:3000/posts?_where={"or":[{"views":{"gt":100}},{"author":{"name":{"lt":"m"}}}]}'
# Posts with (views >= 50) AND (title contains "rust")
curl 'http://localhost:3000/posts?_where={"and":[{"views":{"gte":50}},{"title":{"contains":"rust"}}]}'Use _sort with comma-separated fields. Prefix with - for descending:
# Sort by title ascending
curl http://localhost:3000/posts?_sort=title
# Sort by views descending
curl http://localhost:3000/posts?_sort=-views
# Multi-field sort
curl http://localhost:3000/posts?_sort=author.name,-viewsPage-based (returns envelope with metadata):
curl http://localhost:3000/posts?_page=1&_per_page=10Response:
{
"first": 1,
"prev": null,
"next": 2,
"last": 4,
"pages": 4,
"items": 100,
"data": [...]
}Slice-based (returns plain array with X-Total-Count header):
curl http://localhost:3000/posts?_start=0&_end=10
curl http://localhost:3000/posts?_start=0&_limit=5Both methods include X-Total-Count header with the pre-pagination total.
Search all string fields recursively (case-insensitive):
curl http://localhost:3000/posts?q=helloEmbed child records (hasMany uses {parent}Id convention):
# Attach comments to each post
curl http://localhost:3000/posts?_embed=comments
curl http://localhost:3000/posts/1?_embed=commentsEach post gets a comments array.
Expand parent record (belongsTo uses {parent}Id field):
# Attach author to each post
curl http://localhost:3000/posts?_expand=author
curl http://localhost:3000/comments?_expand=postEach child gets a parent object.
Both can be comma-separated for multiple relations:
curl http://localhost:3000/posts?_embed=comments,tags&_expand=authorDelete a resource and all dependents:
# Delete post 1 and all comments where postId == "1"
curl -X DELETE 'http://localhost:3000/posts/1?_dependent=comments'| Method | Route | Description |
|---|---|---|
| GET | /posts |
List all (supports filtering, sorting, pagination) |
| GET | /posts/:id |
Get single item |
| POST | /posts |
Create item (id auto-generated if absent) |
| PUT | /posts/:id |
Full replace |
| PATCH | /posts/:id |
Partial update |
| DELETE | /posts/:id |
Delete item |
| DELETE | /posts/:id?_dependent=other |
Cascade delete |
| Method | Route | Description |
|---|---|---|
| GET | /profile |
Get singleton |
| PUT | /profile |
Replace singleton |
| PATCH | /profile |
Merge-patch singleton |
| Method | Route | Description |
|---|---|---|
| GET | / |
List all resource names |
| Operator | Example | Description |
|---|---|---|
(none) or :eq |
?title=hello |
Exact equality |
:ne |
?author:ne=bob |
Not equal |
:lt |
?views:lt=50 |
Less than |
:lte |
?views:lte=100 |
Less than or equal |
:gt |
?views:gt=100 |
Greater than |
:gte |
?views:gte=100 |
Greater than or equal |
:in |
?id:in=1,2,3 |
Value in comma-separated list |
:contains |
?title:contains=rust |
Case-insensitive substring |
:startsWith |
?title:startsWith=He |
Case-insensitive prefix |
:endsWith |
?title:endsWith=lo |
Case-insensitive suffix |
| Parameter | Example | Description |
|---|---|---|
_sort |
?_sort=title |
Sort by field(s) |
- (prefix) |
?_sort=-views |
Descending order |
_page |
?_page=1 |
Page number (1-based) |
_per_page |
?_per_page=10 |
Items per page (default: 10) |
_start |
?_start=0 |
Start index (slice-based) |
_end |
?_end=10 |
End index (slice-based) |
_limit |
?_limit=5 |
Limit items (alternative to _end) |
q |
?q=hello |
Full-text search |
_embed |
?_embed=comments |
Include child records (hasMany) |
_expand |
?_expand=author |
Include parent record (belongsTo) |
_where |
?_where={"or":[...]} |
Complex JSON filter |
_dependent |
?_dependent=comments |
Cascade delete dependents |
| Header | Description |
|---|---|
X-Total-Count |
Total items before pagination |
Content-Type |
Always application/json |
| CORS headers | Disabled by default (enable with --cors) |
jsond [OPTIONS] [DB]
Arguments:
[DB] Path to the JSON or JSON5 database file [default: db.json]
Options:
-p, --port <PORT> Port to listen on (0 = random) [env: PORT=] [default: 3000]
--host <HOST> Host address to bind to [env: HOST=] [default: 127.0.0.1]
-s, --static <STATIC> Serve static files from this directory [default: public]
--delay <DELAY> Add artificial delay in milliseconds to all responses [default: 0]
-w, --watch Watch the database file for changes and reload automatically
--cors Enable/Disable CORS headers [default: false]
--readonly Readonly mode: disable POST, PUT, PATCH, DELETE
--ids <ID_STRATEGY> [default: uuidv7] [possible values: int, v4, v7]
--per-page <PER_PAGE> Number of items per page [default: 10]
-h, --help Print help
-V, --version Print version# Basics
jsond db.json
jsond db.json5 # JSON5 input supported
jsond db.json --port 4000 --host 0.0.0.0 # Use custom port and host
jsond db.json --ids v7 # Use uuidv7 for ids. default
jsond db.json --ids v4 # Use uuidv4 for ids
jsond db.json --ids int # Use incrementing integers for ids
jsond db.json --watch # Watch for file changes
jsond db.json --delay 500 # Simulate 500ms network latency
jsond db.json --readonly # Readonly API (no writes)
jsond db.json --static ./public # Serve static files. auto-detected if ./public exists- Field filtering with operators (
:gt,:lt,:contains, etc.) - Nested dot-path filters
- Complex JSON filters with
_where - Multi-field sorting with direction control
- Page-based pagination with
_page+_per_page - Slice-based pagination with
_start/_end/_limit X-Total-Countheader on all list responses- Full-text search with
q - Relations:
_embed(hasMany) and_expand(belongsTo) - Cascade delete with
_dependent - Singleton resources (GET/PUT/PATCH on objects)
- IDs always stored as strings
- Auto-generated IDs on POST (configurable strategy)
- Hot-reload on file changes
- CORS disabled by default
- Static file serving from
./public - JSON5 input format support
- Custom middleware (through code integration)
- API Documentation
- TypeScript definitions
- GraphQL support
By default, jsond serves static files from the ./public directory as a fallback.
mkdir public
echo '<h1>Hello</h1>' > public/index.html
jsond db.jsonAccess at http://localhost:3000/
Change the directory:
jsond db.json --static ./staticWhen creating resources via POST without an id, jsond auto-generates one:
| Strategy | Format | Features | Default |
|---|---|---|---|
v7 |
018e3d8c-… |
Time-sortable, k-sortable in indexes | ✅ |
v4 |
f47ac10b-… |
Random, json-server v1 compatible | ❌ |
int |
1, 2, 3, ... |
Incrementing integers, time-sortable | ❌ |
Use --ids to change:
jsond db.json --ids v4
jsond db.json --ids intRun with --readonly to disable all writes (POST, PUT, PATCH, DELETE):
jsond db.json --readonlyAll write requests return 403 Forbidden.
Run with --watch to automatically reload when the database file changes:
jsond db.json --watchUseful for hand-editing JSON while the server runs.
Run integration tests:
cargo testMIT or Apache-2.0