serve
Use REST as the local app contract. Keep dev tools on their own route base.
The server starts from fixture-backed resources, exposes readable REST routes, supports local writes, and keeps viewer, schema, manifest, import, batch, log, GraphQL, and operation routes explicit. File-like suffixes such as .json and .md choose response formats for the same shaped data.
/db/*
App-facing REST alias for fixture-like reads and writes.
/__db/*
Viewer, schema, manifest, import, batch, logs, events, and scoped REST.
/graphql
Optional GraphQL route for local queries and mutations.
endpoint map
Route families
Defaults shown for 127.0.0.1:7331.
GET /__db
Built-in viewer shell.
GET /__db/schema
Normalized schema metadata.
GET /__db/manifest[.json|.html|.md]
Viewer/API manifest in negotiated or explicit format.
GET /__db/events
Live event stream for viewer refreshes.
GET /__db/log
Runtime log and request trace output.
POST /__db/import
CSV import endpoint used by the viewer.
POST /__db/batch
Sequential REST batch execution.
/__db/rest/*
Scoped REST when the app data alias is disabled or not desired.
/db/*
App-facing REST alias controlled by server.dataPath.
POST /graphql
GraphQL subset endpoint.
POST /__db/operations/:ref
Allowlisted registered operation execution.
collections
Resource CRUD endpoints
documents
Singleton endpoints
formats
What a suffix means on a REST route
It selects an output format
Adding .json, .md, .html, or a registered custom extension to a GET route renders the same resource after REST shaping. It does not fetch a different source file.
Query shaping happens first
select, expand, offset, and limit run before the renderer. /db/users.md?select=id,name is Markdown for the selected fields.
Extensionless reads negotiate
GET /db/users uses the configured default or a registered Accept media type. Unsupported Accept values fall back to the default format.
Writes stay extensionless
Use POST /db/users, PATCH /db/users/u_1, PUT /db/settings, and PATCH /db/settings for JSON request bodies. Suffixes are for read rendering.
Source file extensions and REST response extensions are separate. Built-in source readers load db/*.json, db/*.jsonc, and db/*.csv. Built-in response formats are .json, .html, and .md. Add source formats through sources.readers; add REST response formats through rest.formats.
curl
REST examples
curl http://127.0.0.1:7331/db/users.json
curl 'http://127.0.0.1:7331/db/users.json?select=id,name&offset=0&limit=20'
curl 'http://127.0.0.1:7331/db/users.json?id=u_1&select=id,name'
curl http://127.0.0.1:7331/db/users/u_1.json
curl http://127.0.0.1:7331/db/users/u_1.md
curl http://127.0.0.1:7331/db/settings.html
curl -X POST http://127.0.0.1:7331/db/users \
-H 'content-type: application/json' \
-d '{"id":"u_2","name":"Grace Hopper","email":"grace@example.com"}'
curl -X PATCH http://127.0.0.1:7331/db/users/u_2 \
-H 'content-type: application/json' \
-d '{"name":"Rear Admiral Grace Hopper"}'
curl -X DELETE http://127.0.0.1:7331/db/users/u_2
batch + scoped rest
Same contract, different base
curl http://127.0.0.1:7331/__db/rest/users.json
curl -X POST http://127.0.0.1:7331/__db/batch \
-H 'content-type: application/json' \
-d '[
{ "method": "GET", "path": "/db/users.json" },
{
"method": "PATCH",
"path": "/db/settings",
"body": { "theme": "dark" }
}
]'
curl -X POST http://127.0.0.1:7331/__db/operations/users.get \
-H 'content-type: application/json' \
-d '{"params":{"id":"u_1"}}'
custom extensions
Add output formats in config
A custom format adds routes like /db/users.yaml and media negotiation for extensionless routes. Use object syntax when the format also needs manifest support.
import { defineConfig } from '@async/db/config';
export default defineConfig({
rest: {
formats: {
default: 'json',
yaml: {
mediaTypes: ['application/yaml', 'text/yaml'],
contentType: 'application/yaml; charset=utf-8',
render({ data }) {
return stringifyYaml(data);
},
renderManifest({ data }) {
return stringifyYaml(data);
},
},
},
},
});
custom sources
Add input formats separately
If a project wants to load fixtures from a new file type, register a source reader. That changes how Async DB discovers input files; it does not automatically add a matching REST suffix.
import { defineConfig } from '@async/db/config';
export default defineConfig({
sources: {
readers: [
{
name: 'ndjson-data',
match({ file }) {
return file.endsWith('.ndjson');
},
async read({ basename, readText }) {
const text = await readText();
return {
kind: 'data',
resourceName: basename,
format: 'ndjson',
data: text
.trim()
.split('\\n')
.filter(Boolean)
.map((line) => JSON.parse(line)),
};
},
},
],
},
});
db/users.ndjson can become the users resource, while GET /db/users.md still means “render users as Markdown.”configuration
Move and lock down routes deliberately
export default defineConfig({
server: {
apiBase: '/__db',
dataPath: '/db',
host: '127.0.0.1',
port: 7331,
},
});
export default defineConfig({
server: {
expose: {
rest: 'registered-only',
graphql: false,
viewer: 'dev',
schema: 'dev',
manifest: 'dev',
},
},
});
Set server.dataPath: false when an app should not receive the /db alias. The same REST resources remain available under /__db/rest for local tools, and standalone serve keeps root convenience routes for development.