Each team finds what it can use, and nothing else on the database
The data team publishes saved queries into collections. Each team's role grants its collections, read-only, with its own rate limit, so a key reaches exactly those queries and never the tables behind them. /catalog shows each team what it can call and on what terms, and a query filed into a collection is there on the next call.
On the same connection, in no published query: no team reaches them.orders.amt: Net revenue in EUR, refunds excludedsource: databaseimported from the database's comments
What finance's /catalog saysonly what this key can run
yours, outside QueryAPIGateyoursservedrefusedA key made from a role copies it once; people who sign in use the role live.
Your teamsA key per team, or people signing inEach one a team's role: its collections, rate limit and quota.
↓
The governed coreCaller → reach → termsOnly the queries in the role's collections, read-only. Anything else, a query outside them or ad-hoc SQL, is 403.
↓
CollectionsWhat the data team publishedFiling a query into a collection puts it in every granted team's catalog at once, and the move is audited.
↓
Your warehouseReached only through published queriesTables in none of them stay out of reach on the same connection.
↓
/catalogWhat can I use, and on what termsEach query's description, parameters and cache, and the caller's own rate limit.
Self-serve data for every team, without handing out the database
Ten teams want data from the warehouse. Shared database passwords leak, and routing every request through the data team as a ticket doesn't scale. Publish saved queries in collections instead, give each team a role, and let people find what they can use.
✓A role per team: the collections it may run, its rate limit and quota. Keys are made from the role, or people sign in through your identity provider and are mapped to it.
✓A collection groups saved queries. Granting one is read-only and reaches exactly the queries in it, nothing else on the connection.
✓GET /catalog shows each caller what it can call and on what terms: description, parameters, whether it is cached or writable, and its own rate limit.
✓Descriptions imported from database comments say what each column means, and every run is attributed to the team that made it.
# a role for the finance team: one collection, read-only, a budget
curl -X POST https://data.example.com/api/v1/roles \
-H 'X-API-Key: <admin>' -H 'Content-Type: application/json' -d '{
"name": "finance", "connections": [],
"collections": ["finance"], "rate_limit": "600/hour"
}'
# what a finance key finds
GET /catalog
{"queries": [{"name": "revenue_by_region", "collection": "finance",
"description": "Net revenue by region and month",
"cache_ttl": 300, "can_write": false}, ...],
"caller": {"name": "finance-reporting", "rate_limit": "600/hour", ...}}
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.