REST as the Universal Database Interface: One Engine, Three Protocols
A modern application has three kinds of callers into its database. A long-lived server wants a persistent connection, connection reuse, and the streaming protocols SQL was built around. A serverless worker or edge function wants to make a request, get an answer, and exit. An AI agent wants to discover what the database can do and call those operations as tools, without any custom glue.
Traditionally these three callers speak to different pieces of infrastructure: the server connects to Postgres, the serverless worker talks to a REST wrapper somebody built in front of Postgres, and the agent talks to a "tool server" somebody built to translate agent calls into SQL. Three services, three auth models, three failure surfaces, three things that can drift out of sync.
HeliosDB collapses that into one engine with three protocol surfaces: the PostgreSQL wire protocol on 5432, a REST API under /api/v1, and a Model Context Protocol endpoint under /api/v1/mcp. Every caller picks the surface that fits, talks to the same data, and authenticates through the same key model. This article argues for that composition and shows exactly when each protocol is the right choice.
Three Callers, Three Needs, One Backend
| Caller | Best surface | Why |
|---|---|---|
| Long-lived server (background worker, analytics job) | PostgreSQL wire (5432) | Streaming, COPY, LISTEN/NOTIFY, session-scoped state |
| Serverless / edge function | REST (/api/v1) | Stateless, no pool, auth as a header, cold-start friendly |
| Mobile / browser client | REST with scoped key | No driver, HTTPS egress only, revocable per-user key |
| LLM agent (Claude, Cursor, custom) | MCP (/api/v1/mcp) | Tool discovery, JSON-RPC, structured arguments |
| Ingest pipeline (high throughput) | Wire + COPY | Orders-of-magnitude faster than row INSERTs |
Why REST Wins for the Stateless Frontline
Serverless compute — AWS Lambda, Cloudflare Workers, Vercel Functions, Fly Machines, Knative jobs — is the default for new workloads. The operational model is "spin up, do one thing, exit, maybe exit hard." Traditional databases were not designed for callers that behave this way, and every serverless-to-Postgres story eventually becomes a connection-pool story.
The REST surface dissolves four of the worst problems:
- No connection pool on the caller. Every invocation is an HTTP request. The engine handles pooling internally. Connection limits stop being a function of your Lambda concurrency.
- No reconnect storms on cold start. HTTPS connection reuse is a browser / CDN concern. A freshly-spawned function that makes one POST and exits never pays TCP setup three times.
- Auth is a header. No rotating-secret injection sidecar, no cloud-IAM token exchange at the start of every function, no connection-string rebuild.
X-API-Key: hdb_…and you're in. - Edge-friendly. Cloudflare Workers disallow raw TCP in many contexts; HTTPS is universal.
This is why the rise of "serverless-compatible Postgres" products has been a rise of REST-in-front-of-Postgres adapters. HeliosDB skips the adapter by making REST a first-class surface on the engine itself.
Per-Database API Keys Replace a Layer of IAM
The standard cloud-database answer to "who can do what" is IAM: assign a role to your function's execution identity, grant that role database access, rotate the role's credentials, audit IAM policy changes. It works and it is expensive — expensive in complexity, in audit surface, and in the blast radius of a misconfiguration.
HeliosDB scopes authorisation at the database level, not the cloud-account level. Every database has its own keyring. Keys are scoped read, write, or admin. Issuing a key is a POST; revoking one is a DELETE. The blast radius of any key is exactly one database.
# Issue a read-only key for a dashboard
curl -X POST .../api-keys \
-H "X-API-Key: $ADMIN_KEY" \
-d '{"name":"dashboard-prod","scopes":["read"]}'
# Issue a write-only key for an ingest worker pool
curl -X POST .../api-keys \
-H "X-API-Key: $ADMIN_KEY" \
-d '{"name":"ingest-us-east","scopes":["write"]}'
# List and revoke
curl .../api-keys
curl -X DELETE .../api-keys/$KEY_ID
Some consequences that take a while to fully appreciate:
- Rotation doesn't need a deploy. Issue the new key, hand it to the worker pool via your secret store, revoke the old one. The app never restarts.
- Per-customer isolation is cheap. One database per customer, one admin key per customer, their data can't leak through a shared role because there is no shared role.
- The audit trail is the keyring. Listing keys tells you who can see this database. There is no parallel universe in IAM to cross-reference.
OpenAPI 3.1 Gives You Clients for Free
The entire REST surface is described by an OpenAPI 3.1 spec served at /api/v1/openapi.json. This is not a hand-written document that lags the implementation; it's the source of truth the server validates requests against. Any OpenAPI-aware tool can consume it:
# Typed TypeScript client
npx openapi-typescript https://cloud.heliosdb.com/api/v1/openapi.json \
-o src/heliosdb.d.ts
# Python client
openapi-python-client generate \
--url https://cloud.heliosdb.com/api/v1/openapi.json
# Go, Java, Rust, Kotlin, Swift, C# — pick your poison
openapi-generator-cli generate -g go \
-i https://cloud.heliosdb.com/api/v1/openapi.json \
-o ./heliosdb-go
An SDK maintained by us would always lag the server. An SDK generated by you from the spec can't. Versioning moves from SDK-version-vs-API-version bookkeeping (always painful) to "regenerate when you upgrade."
MCP Turns the Database Into a Tool for Agents
Model Context Protocol is a discovery-and-invocation standard for LLM tools: an MCP-aware agent asks a server what tools exist, and the agent can call those tools with structured arguments. The shape is JSON-RPC 2.0. HeliosDB exposes every meaningful database operation as a tool:
Updated Oct 2026: the MCP endpoint now requires an API key. Send the hdb_... key returned by POST /api/v1/quick-start (or generated per database in the console) as Authorization: Bearer hdb_... or X-API-Key: hdb_...; without it the endpoint answers 401. Account creation moved out of MCP: quick_start is now REST-only. Since 2026-10-02 the endpoint is a standard MCP server (Streamable HTTP, protocol 2026-07-28 and 2025-03-26 to 2025-11-25, stateless): Claude Code, Cursor, VS Code, the MCP Inspector and the official SDKs connect to it directly with the key as a header, and all 27 listed tools work over MCP, including list_databases, create_database and delete_database. A refused call comes back as an isError result with the reason.
curl -X POST https://cloud.heliosdb.com/api/v1/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $HDB_API_KEY" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}' | jq
# Available tools (excerpt; 27 in total, no quick_start)
[
{ "name": "execute_sql", "description": "Execute a SQL query on a HeliosDB database." },
{ "name": "list_branches", "description": "List all branches on a database." },
{ "name": "create_branch", "description": "Create a new branch (Pro+). ..." },
{ "name": "switch_branch", "description": "Switch the active branch on a database." },
{ "name": "merge_branch", "description": "Merge one branch into another." },
{ "name": "query_at", "description": "Execute a time-travel query at a specific point in time." },
{ "name": "create_api_key", "description": "Create a new API key scoped to a specific database." },
{ "name": "vector_search", "description": "Vector similarity search (HNSW). Pro+ required." },
...
]
Invoke one:
curl -X POST https://cloud.heliosdb.com/api/v1/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $HDB_API_KEY" \
-d '{
"jsonrpc":"2.0",
"method":"tools/call",
"params":{
"name":"execute_sql",
"arguments":{
"org_id":"...",
"db_id":"...",
"sql":"SELECT count(*) FROM notes"
}
},
"id":2
}'
An agent building a feature can now do things that used to require a human: create a branch, run a migration, test against the branch, merge on success, rollback on failure. The tools compose the way the REST API does, because they are backed by the same REST API.
One Engine, Three Surfaces, Three Tiers
The three-surface contract holds across HeliosDB's deployment tiers. You do not get REST on some engines and not others. You do not get MCP only on Cloud. The same engine speaks all three:
| Tier | Wire (5432) | REST | MCP | Typical deployment |
|---|---|---|---|---|
| Nano | ✓ | ✓ | ✓ | Embedded, edge, CI, single container |
| Lite | ✓ | ✓ | ✓ | Single-node production, edge + HeliosProxy |
| Full | ✓ | ✓ | ✓ | Distributed cluster, multi-region |
| Cloud (managed) | ✓ (paid tiers) | ✓ | ✓ | Zero infra, free tier, graduate to self-host |
An application targeting the REST contract is portable across the whole matrix. A long-lived worker targeting the wire protocol is also portable — the wire protocol is the standard PostgreSQL one, so every driver in every language already works. An agent targeting MCP is portable because MCP is an open protocol, not a vendor dialect.
Bootstrapping Across All Three Surfaces
Provision once:
curl -X POST https://cloud.heliosdb.com/api/v1/quick-start \
-H "Content-Type: application/json" \
-d '{"email":"me@app.com","password":"...","username":"me","org_name":"demo","db_name":"main"}'
# -> token (24-hour session JWT), api_key (hdb_..., valid 365 days), org_id, db_id, endpoints
export HDB_TOKEN=... # .data.token
export HDB_API_KEY=... # .data.api_key
Your long-lived worker connects over the wire protocol. Free quick-start accounts are REST and MCP only (no public Postgres port); on paid tiers, GET /api/v1/connection-info returns the DSN for your database, and a self-hosted Nano, Lite or Full takes the same psql connection:
curl -s https://cloud.heliosdb.com/api/v1/connection-info \
-H "Authorization: Bearer $HDB_TOKEN" | jq
psql "<connection_string from the response>"
Your serverless function calls REST:
await fetch(`${EXECUTE_URL}`, {
method: "POST",
headers: { "X-API-Key": process.env.HDB_API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ sql: "SELECT * FROM notes WHERE user_id = $1", params: [uid] })
});
Your agent calls MCP:
# Claude Code
claude mcp add --transport http heliosdb https://cloud.heliosdb.com/api/v1/mcp \
--header "Authorization: Bearer $HDB_API_KEY"
// Cursor / VS Code mcp.json
{
"mcpServers": {
"heliosdb": {
"url": "https://cloud.heliosdb.com/api/v1/mcp",
"headers": { "Authorization": "Bearer hdb_..." }
}
}
}
Use the hdb_... API key in a persistent agent config: the quick-start session token expires after 24 hours, the key is valid for 365 days and can be replaced in the console (Databases > API Keys).
Same database. Same data. Same authorisation model (keys and scopes). Three protocols, each picked to fit the caller.
The Scalability Composition
The architectural payoff is a composition most database stacks cannot make:
- Stateless REST frontline scales horizontally with your serverless platform. More traffic = more workers = more requests. No connection-pool ceiling.
- Long-lived wire-protocol workers handle streaming, ingest, and cursor-based jobs against the same data.
- MCP tool surface lets agents operate the database directly, bypassing the custom "AI backend" that most AI features end up building.
Each of the three surfaces is good at what the other two are bad at. Picking one per caller is not a compromise — it's the whole point. The database doesn't need to pretend that all callers have the same shape.
What This Costs You
Two things, worth naming honestly:
- A multi-surface engine is more complex than a single-protocol engine. HeliosDB carries PostgreSQL wire parsing, a REST server, and an MCP JSON-RPC handler. That complexity is ours, not yours — but it's real, and it's why we invest in the protocol-level test matrix we do. A team building on top of this benefits from the composition and does not pay the complexity.
- MCP is a young standard. It's open, it's JSON-RPC, and it's already supported by the major agent frameworks — but it will evolve. We track the spec; applications built against our MCP endpoint should be prepared to benefit from new tool shapes over time.
When You Build on This
The decision the three-surface architecture pushes you toward is: stop making one protocol do the job of three. Don't wrap Postgres in a homegrown REST adapter for your serverless tier; don't build a one-off agent toolbox when MCP already exists; don't force a streaming analytics job through HTTP because that's what the rest of the stack uses.
Pick the surface that fits the caller. Share the engine, the data, and the authorisation. Let the complexity live in the database, where it belongs.
This post concludes the BaaS architecture series. Part 1: Database-Agnostic by Default. Part 2: Zero to a Vector-Search Notes App. Reference material: BaaS page, OpenAPI 3.1 spec, and the MCP endpoint at /api/v1/mcp.