Skip to content
Browse docs

Serverless and edge access

Edge and serverless runtimes like Vercel Edge Functions, Cloudflare Workers, and Deno Deploy cannot open raw TCP sockets, so a normal connection string will not work there. Layerbase gives you three HTTP paths that do: the HTTP query API for every engine, an Upstash-compatible REST endpoint for Redis and Valkey, and a PlanetScale serverless driver for MySQL and MariaDB.

Serverless runtimes that can open sockets: use the pooled string

Node-based serverless platforms (Vercel Functions, AWS Lambda, Google Cloud Run, Netlify Functions) are not the same as edge runtimes: they can open raw TCP sockets, so a normal driver and connection string work fine. For those, always use the pooled connection string, never the direct one. The Connect dialog's Pooled toggle is on by default, so copy the string with it left alone.

The pooler is what keeps a fleet of concurrent function instances from opening one real database backend each. There is a second reason that is easy to miss: a database's direct port enforces a hard platform limit of 20 new connections per 10 seconds per source IP. Serverless platforms route outbound traffic through a small shared pool of NAT addresses, so under a burst many instances arrive from one IP, trip that limit, and get connections refused. It presents as an intermittent outage rather than a quota message. For the Postgres-wire engines the pooled string routes over the shared 5432 endpoint, which is not subject to that per-port cap. MySQL and MariaDB have no shared endpoint, so their pooled string still uses a dedicated port and the same limit applies: keep connection churn low there too.

The common advice to keep a separate direct connection string for schema migrations does not apply to Layerbase. That guidance exists because some poolers cannot carry the statements a migration performs. Payload CMS and Drizzle migrations have both been verified end to end through our transaction-mode PgBouncer pooling, so point your migration runner at the same pooled string as the rest of the app instead of wiring a second credential into your platform.

The HTTP query API (every engine)

Every database accepts queries over HTTP at POST /v1/databases/:id/query. You authenticate with a Bearer API key, send a JSON body, and get JSON back. This is the simplest path from an edge function because it needs nothing but fetch. The exact URL for your database is in the HTTP API snippet under the Connect dialog's Snippets tab, so copy it from there rather than hand-building the host.

SQL-mode engines take a query string. REST-mode engines (Qdrant, Meilisearch, CouchDB, Weaviate) take an http object with a method and path instead. TigerBeetle uses a binary protocol and is not reachable over this API.

curl
curl -s https://<your-cloud-host>/v1/databases/<id>/query \
  -H "Authorization: Bearer YOUR_DB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "SELECT 1"}'
Edge function (fetch)
const res = await fetch(
  'https://<your-cloud-host>/v1/databases/<id>/query',
  {
    method: 'POST',
    headers: {
      Authorization: 'Bearer YOUR_DB_API_KEY',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ query: 'SELECT * FROM users LIMIT 10' }),
  },
)
const data = await res.json()

A successful SQL query returns columns and rows:

Response
{
  "columns": ["id", "email"],
  "rows": [
    [1, "ada@example.com"],
    [2, "grace@example.com"]
  ],
  "rowCount": 2
}

For a REST-mode engine, send the request as an http object and the engine's own JSON comes back on the http field:

REST-mode body (Qdrant, Meilisearch, CouchDB, Weaviate)
{ "http": { "method": "GET", "path": "/collections" } }

API keys

The query API needs a Bearer key. Keys are prefixed sk_, and the full secret is shown exactly once when you create or rotate it. Layerbase stores only a hash, so copy it into your secret store right away. There is no way to see it again.

There are two kinds. A per-database key lives on the database's Connect dialog under the API key tab. It is not minted automatically: click Generate API key when you want HTTP access, so a fresh database page carries no secret you could leak on screen. An account key (called a personal API key) lives at /cloud/settings and works across all your databases plus the rest of the cloud API.

Both keys support Rotate (issue a new secret) and Revoke (disable the key). Revoking takes effect immediately: anything still using that secret gets a 401. Rotation differs by key type: rotating a per-database key lets the old secret keep working for 24 hours before it starts returning 401, while rotating an account key revokes the old secret immediately. Keys show a last-used time so you can spot stale ones.

Redis and Valkey over REST (Upstash-compatible)

Redis and Valkey databases also expose an Upstash-compatible REST endpoint. The Connect dialog shows a REST URL and a REST Token when this is available. Use them with the @upstash/redis client (or @vercel/kv, which takes the same url and token), both of which run in edge runtimes.

@upstash/redis
import { Redis } from '@upstash/redis'

const redis = new Redis({
  url: 'YOUR_REST_URL',
  token: 'YOUR_REST_TOKEN',
})

await redis.set('key', 'value')
const value = await redis.get('key')

You can also hit it directly with a Bearer POST, sending the command as a JSON array:

curl (REST)
curl -X POST YOUR_REST_URL \
  -H "Authorization: Bearer YOUR_REST_TOKEN" \
  -d '["GET", "mykey"]'

MySQL and MariaDB serverless driver (PlanetScale)

MySQL and MariaDB databases expose a PlanetScale-compatible serverless HTTP endpoint. The Connect dialog shows a PS URL, PS Username, and PS Password. Use them with @planetscale/database or the Drizzle planetscale-serverless adapter, both built for edge functions.

@planetscale/database
import { connect } from '@planetscale/database'

const conn = connect({
  host: 'YOUR_PS_URL'.replace('https://', ''),
  username: 'YOUR_PS_USERNAME',
  password: 'YOUR_PS_PASSWORD',
})

const result = await conn.execute('SELECT * FROM users')
Drizzle ORM
import { drizzle } from 'drizzle-orm/planetscale-serverless'
import { connect } from '@planetscale/database'

const connection = connect({
  host: 'YOUR_PS_URL'.replace('https://', ''),
  username: 'YOUR_PS_USERNAME',
  password: 'YOUR_PS_PASSWORD',
})

const db = drizzle(connection)

Postgres from Deno and Supabase Edge Functions

Postgres does not have an HTTP variant, but Deno and Supabase Edge Functions can open a TLS socket, so you connect with a normal Postgres driver over the pooled connection string. If your database requires a client certificate, use the @layerbase/deno-mtls adapter against the direct-TLS port so the certificate is actually presented. See the client-certificates guide for the full setup.

Deployment environments with dynamic egress

If a deployment environment keeps one public egress IP for its lifetime but may receive a new one on the next deployment, the Layerbase Cloud allow-current-ip.sh helper can detect that IP and idempotently add it to a database's firewall. Run it during deployment or startup with LAYERBASE_API_KEY and LAYERBASE_DATABASE_ID set as server-side secrets. It requires curl and jq.

Download, then run during deployment or startup
curl --fail --show-error --location \
  --output allow-current-ip.sh \
  https://layerbase.com/scripts/allow-current-ip.sh
chmod 700 allow-current-ip.sh

# Set these in your platform secret store:
# LAYERBASE_API_KEY
# LAYERBASE_DATABASE_ID
./allow-current-ip.sh

The helper does not enable IP restrictions or remove stale entries. For per-request or per-instance rotating egress, use published CIDR ranges, mTLS where supported, or a managed static-egress proxy when one is available. See the IP allowlisting guide for setup and safety details.

Keep tokens server-side

The API keys, REST tokens, and PS credentials are all write-capable: anyone holding one can read and modify your data. Store them as server or function secrets and never ship them to a browser bundle. If a token leaks, rotate or revoke it from the Connect dialog or /cloud/settings.