jevql docs
playgroundGitHub ↗

Deploy a node

Run jevql serve as a shared service for one Postgres, with a bearer token, on Fly.io or any container host.

A node is jevql serve in a container: one HTTP API and one MCP endpoint in front of one Postgres, with a shared answer cache. Clients only need the URL and a token.

Launch it on Fly.io

From a clone of the repository, fly launch reads deploy/fly.toml, asks for a name and region, creates the app and the cache volume, and deploys:

git clone https://github.com/kylemclaren/jevql && cd jevql
fly launch --config deploy/fly.toml --no-deploy
fly secrets set DATABASE_URL='postgres://user:pass@host:5432/db' \
                TYPESAFE_API_KEY='tsk_...' \
                JEVQL_TOKEN="$(openssl rand -hex 32)"
fly deploy

Fly terminates TLS at its edge, so the token only travels over HTTPS. The volume declared in the config keeps the answer cache across deploys.

Or any container host

docker build -f deploy/Dockerfile -t jevql .
docker run -p 8080:8080 -v jevql_data:/data \
  -e DATABASE_URL=... -e TYPESAFE_API_KEY=... -e JEVQL_TOKEN=... jevql

Put it behind TLS. jevql serve refuses a non-loopback address without a token unless you pass --insecure.

Use it

curl -H "Authorization: Bearer $JEVQL_TOKEN" https://<your-app>.fly.dev/v1/health
const db = new Jevql({ url: "https://<your-app>.fly.dev", token: process.env.JEVQL_TOKEN })
db = Jevql(url="https://<your-app>.fly.dev", token=os.environ["JEVQL_TOKEN"])

MCP clients connect to https://<your-app>.fly.dev/mcp with the same bearer token. The full API is at /openapi.json.

Auth

One bearer token per node, set with JEVQL_TOKEN or --token. Everything except /openapi.json requires it. The token is compared in constant time. There is no per-user auth in v1; put an identity-aware proxy in front if you need one.

An open node

A demo or a public playground does not want a token. Set JEVQL_INSECURE=1 (or pass --insecure) and the node answers anyone. Pair it with the other knobs so an open node stays cheap and read-only:

JEVQL_INSECURE=1 serve without a bearer token
JEVQL_CORS=https://example.com,https://other.example origins allowed to call the API from a browser (* for any)
JEVQL_RATE_LIMIT=60 requests per minute per client IP; excess gets 429 with code rate
JEVQL_MAX_ROWS=300 cap on rows judged by one query; more returns 402 with code budget
a read-only database role the only thing that stops a DELETE

The public playground at jevql.fly.dev/playground runs exactly this: a node with no token, a reader role, a rate limit and a row cap. Its settings drawer lets you point the playground at your own node instead.

What the node holds

DATABASE_URL the one Postgres it serves; read-only credentials are a good idea
TYPESAFE_API_KEY held server-side; clients never see it
/data/cache.db the shared answer cache on the volume
JEVQL_ALLOW_CACHE_ADMIN=1 enables GET/DELETE /v1/cache
--allow-writes lets MCP tools run non-SELECT statements (off by default)

Edit this page on GitHub ↗