Use casesProduct teamsInternal tools02 / 12
How it fits together

One URL for the whole app, and each person sees their own rows

The browser calls QueryAPIGate with the token your users already sign in with. The token says who they are and which role they have, fills the query's filter, and picks the cache entry. Six saved queries across three databases replace the backend service you would otherwise write and run.

3databases, one base URL
8output formats
0keys in the page
Your internal app
Front door
The governed core
Your data
Identity provider↗
JWKS · keys cached
Shared state↗
Redis · cache · limits
tools.internal
↗ bbob My open tickets
Download CSV last response
↗

REST

GET /q/<name>
CORS: tools.internal✓ Bearer token, no key✓

one base URL for every query

The governed corerole tools-staff · live↗

01 · Identity

Verify the token
signature, expiry, issuer, audience; keys from your provider, cached
Name the caller
jwt:bob@tools.internal

02 · Policy

Its role's grants
these six queries; editing the role applies on the next request
Fill :email from the token
never from the request
:email ← bob@…

03 · Execution

Cache, or run
one entry per query, version, values, format and page
Page or stream
X-Has-More, or ?stream=true for the CSV button
HIT
↗

PostgreSQL

ticketsmy_open_tickets
↗

ClickHouse

eventsweekly_signups
↗

Files, through DuckDB

s3://finance/*.parquetinvoices_2025
What a user can't changethe filter comes from the token
WHERE assignee_email = :email "email": {"type": "str", "from_claim": "email"} GET /q/my_open_tickets?email=carol@tools.internal → 400 param_from_claim
Stablecompatibility promise Expmay change in a minor release Any key in a page's JavaScript is public: sign users in, or keep keys on a server.
Your internal appA browser, no backendIt calls with the user's own sign-in token; CORS allows its origin, and no API key is in the page.
Front doorREST · one base URLEvery query is GET /q/<name>, paged, or streamed for downloads, in eight formats.
AsksIdentity provider · RedisSigning keys are fetched once and cached. Redis, if you run it, shares the cache and the rate limits across instances.
The governed coreToken → role → your own rowsThe token is verified, the user's role grants the six queries, and the filter is filled from the token, never the request. A cached answer is kept per user's values.
Your dataPostgreSQL, ClickHouse, ParquetThree sources behind one URL, through one key-free page.

The same governed core as the full architecture.

For internal tools and dashboards

Back an internal app without a backend team

A frontend needs six queries from three databases. That is six endpoints, three drivers and one more service to keep alive. Or it is six saved queries, one key with JWT sign-in for your app's users, and CORS.

✓App users sign in with your existing JWT (JWKS); a from_claim parameter fills the filter from their token, so each person sees their own rows.
✓Queries across PostgreSQL, ClickHouse and a bucket of Parquet behind one base URL and one key.
✓A cache_ttl per query, shared through Redis when you run more than one instance.
✓Pagination, eight output formats and ?stream=true for the CSV download button.
Internal tools
# the browser calls with the user's own token
fetch("https://data.internal/q/my_open_tickets?page_size=50", {
  headers: {Authorization: "Bearer " + session.idToken}
})

# the saved query: :email comes from the token, never the request
SELECT id, title, status, updated_at
FROM tickets
WHERE assignee_email = :email
ORDER BY updated_at DESC

"query_parameters": {"email": {"type": "str", "from_claim": "email"}}

# server side
QUERYAPIGATE_JWT_JWKS_URL=https://login.example.com/.well-known/jwks.json
QUERYAPIGATE_CORS_ORIGINS=https://tools.internal
More use casesAll twelve →

Not sure which fits?

The example APIs ship with a generated database and one scoped key per scenario, so you can try each of these in a few minutes without connecting anything of your own.

Run the examplesCompare alternatives