Restly is a no-configuration JSON document store. It runs as one Rust binary,
persists data on the local filesystem, exposes convention-based REST resources
under /data/**, and includes an embedded admin UI for monitoring, editing,
and exercising the API.
No collection definitions are required. A collection is created the first time a document is written to it and is removed when it no longer contains any documents or populated nested collections.
cd ui && npm install && npm run build && cd ..
cargo run --releaseOpen http://127.0.0.1:8080. Restly creates a local
data/ directory beside the binary's working directory when the first
document is stored. That directory is intentionally ignored by Git.
During development, run cargo run on port 8080 and cd ui && npm run dev
on port 5173. The Vite server proxies /api, /data, and /ws.
All application documents live under /data. /api/** is reserved for
Restly's monitoring and admin APIs, while / serves the admin UI.
| Method | Path | Operation |
|---|---|---|
| GET | /data |
List discovered collections |
| GET | /data/{collection} |
Find documents |
| POST | /data/{collection} |
Insert a document |
| GET | /data/{collection}/{id} |
Find one document |
| PUT | /data/{collection}/{id} |
Replace or upsert a document |
| PATCH | /data/{collection}/{id} |
Apply a JSON merge patch |
| DELETE | /data/{collection}/{id} |
Delete a document |
| GET/POST | /data/{collection}/{id}/{sub_collection} |
Nested collection operations |
For example:
curl -X POST http://127.0.0.1:8080/data/users \
-H 'content-type: application/json' \
-d '{"name":"Ada","active":true,"profile":{"level":4}}'
curl 'http://127.0.0.1:8080/data/users?where.active=true&sort=-_updatedAt&limit=25'
curl -X PUT http://127.0.0.1:8080/data/users/ada \
-H 'content-type: application/json' \
-d '{"name":"Ada Lovelace"}'
curl -X POST http://127.0.0.1:8080/data/users/ada/orders \
-H 'content-type: application/json' \
-d '{"total":32,"currency":"GBP"}'Documents must be JSON objects. Restly supplies _id, _createdAt, and
_updatedAt; nested documents also receive _parent. Fields beginning with
_ are reserved.
GET /data/{collection} accepts these query parameters:
| Parameter | Example | Meaning |
|---|---|---|
limit |
limit=50 |
Results per page, 1-1000, default 50 |
cursor |
cursor=d_... |
Continue after the returned nextCursor |
sort |
sort=-createdAt,name |
Comma-separated fields; - is descending |
where.{field} |
where.active=true |
Exact match; dot paths are supported |
where.{field}.{op} |
where.price.gte=10 |
eq, ne, gt, gte, lt, lte, in, contains, prefix, exists |
List responses contain { data, page, total }. Every scalar document field,
including nested scalar fields, is automatically indexed in memory for exact
matches. Indexes are rebuilt from local data when a collection is loaded, so
they never need configuration or separate index files.
Collections are stored below data/collections/ in directories that mirror
their resource path. Writes are appended to a JSONL write-ahead journal and
flushed before the in-memory state changes. After 100 changes, Restly writes a
compact snapshot.json and clears that journal. On startup, Restly rebuilds a
collection from its snapshot and journal, recovering committed writes without
an external database. Empty collection directories are pruned automatically;
writing a document to that path later recreates the collection.
The embedded UI has a dashboard, Finder-style collection/document editor, a request workspace, and an observability console. The request workspace keeps saved requests, variables, local run history, assertions, and response views in the browser. The observability console shows the latest 500 server requests without recording query strings or request bodies. Its supporting endpoints stay outside the data namespace:
| Method | Path | Purpose |
|---|---|---|
| GET | /api/health |
Server liveness and version |
| GET | /api/config |
UI bootstrap configuration |
| GET | /api/metrics |
Request, connection, and uptime metrics |
| GET | /api/requests?limit=100 |
Recent method, path, status, duration, and timestamp traces |
| GET | /api/stats |
Collection/document totals and data directory |
| GET | /api/collections |
Collection names, counts, and automatic indexes |
| POST | /api/maintenance/compact |
Compact all loaded collections |
| GET | /ws |
Live metrics and document activity |
Errors use a consistent JSON envelope:
{ "error": { "code": "NOT_FOUND", "message": "document x does not exist", "status": 404 } }config.toml controls only server, logging, and UI presentation settings.
It is not used to declare schemas, collections, indexes, or routes. CLI flags
can override the host, port, logging level, access-log path, and config path.
cargo fmt --all -- --check
cargo clippy --all-targets -- -D warnings
cargo test
cd ui && npm test && npm run buildReleases use a reviewable version-bump pull request; GitHub Actions never
commits directly to main. Before the first release, add a fine-grained
repository secret named RELEASE_BOT_TOKEN with Contents, Pull requests,
and Issues read/write permissions. A GitHub App installation token with the
same permissions also works.
Run Prepare release from main and select patch, minor, or major.
It creates a release/vX.Y.Z branch and a labeled pull request that updates
the Rust and UI package versions together. The normal required checks and
review rules apply to that pull request.
When the release pull request is merged, Publish release tags that exact merge commit, builds the platform archives, and creates or updates the GitHub Release. A rerun resumes publication only when the existing tag already points to the same merge commit; it refuses a tag that points elsewhere.
Import Restly.postman_collection.json into Postman and run the requests in order. It uses a unique postman-demo-* collection for each run, validates the main REST API workflows, and removes its demo documents and collection during cleanup.